Update documentation (second commit)

This commit is contained in:
JeremyStar™ 2024-04-08 20:14:38 +02:00
parent 330bb98dd9
commit 5fed9c1f9b
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
27 changed files with 570 additions and 190 deletions

View file

@ -19,6 +19,7 @@
##
## This script is used to test CORE's behaviour during development.
## It's state should be reverted to how it was before committing (if modified).
## For more permanent tests write a unit test instead.
extends Node
# CORE Configuration

View file

@ -1,22 +1,82 @@
---
sidebar_position: 1
sidebar_position: 0
description: This is an easter egg.
---
# About the CORE Framework
The CORE Framework aims at simplifying development for developers writing their code in the [Godot Engine](https://godotengine.org), version *4.2*.
<!-- StarOpenSource Sky verification links -->
<link href="https://sky.staropensource.de/@staropensource"/>
<link href="https://sky.staropensource.de/@soscore"/>
<!-- StarOpenSource Sky verification links -->
## Why does it exist?
I ([JeremyStarTM](https://jstm.staropensource.de)) didn't want to write a new base every time I started another project. And that's why I made the CORE Framework, to serve as a common base for all my projects.
I ([JeremyStarTM](https://git.staropensource.de/JeremyStarTM)) always wrote a new base for every project... which was not only wasting my time as I've rewritten the same stuff multiple times, often times not better than the last. And that's why I created the CORE Framework, to serve as the common base for my projects. My goal is to improve and push it as far as I can to make it a good and polished base for everyone.
## Should I use it?
If you want to use the CORE Framework in a new project, then your answer is **yes**! If you want to use the CORE Framework in an already existing project, be prepared to do some major refactoring. CORE implements it's own logging infrastructure, scene management, etc. and is not compatible with Godot's builtin/custom solutions. You can toggle them in the configuration file however.
### New projects
Your answer is *(probably)* **yes**!
### Existing projects
**No**, *unless* you want to do some major refactoring of your codebase. CORE implements it's own logging infrastructure, scene management and much more requiring you to essentially rewrite everything from scratch.
## Roadmap
- [x] Configuration support
- [x] Logger implementation
- [x] HTTP Request helper
- [ ] Mod Loader
- [x] Support for custom modules
- [ ] Extensible debugging console
## Comparison
:::note
This table only compares frameworks that are relatively up to date with the Godot Engine (versions 4.1 and 4.2), can be found in the Godot Asset Library or are well known and are aimed at a specific game genre or general usage. Things like animation or dialogue frameworks are not in compared here.
:::
<table>
<tr>
<th></th>
<th>CORE Framework</th>
<th><a href="https://github.com/Oplexitie/Cupcakes-Framework">Cupcakes Framework</a></th>
</tr>
<tr>
<td>Godot Versions</td>
<td>4.2</td>
<td>3.0 until 4.2</td>
</tr>
<tr>
<td>License</td>
<td>GNU AGPL v3</td>
<td>MIT</td>
</tr>
<tr>
<td>Commercial usage</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Oriented at</td>
<td>General usage</td>
<td>FNAF-inspired games</td>
</tr>
<tr>
<td>Implements base features</td>
<td>For apps and games</td>
<td>FNAF-like game mechanics, no base for game though</td>
</tr>
<tr>
<td>Customizable</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Logger</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Scene Management</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Character AI</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Made by</td>
<td><a href="https://staropensource.de">The StarOpenSource Project</a></td>
<td><a href="https://github.com/Oplexitie">Oplexitie</a></td>
</tr>
</table>

View file

@ -1,6 +1,6 @@
{
"label": "Getting started",
"position": 3,
"position": 2,
"link": {
"type": "generated-index",
"description": "Helps you get CORE set up in your project."

View file

@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 1
description: CORE needs to be initialized before usage.
---
@ -10,22 +10,22 @@ This example should explain everything:
```gdscript
extends Node
# CORE Object
var core: Core
# CORE Logger
## Gets a so called "logger instance". It basically saves you a argument on each log call.
## Gets a so called "logger instance". It basically saves you an argument on each log call.
@onready var logger: CoreLoggerInstance = core.logger.get_instance("path/to/script.gd")
# Preinitialization phase
func _init() -> void:
# Create new CoreConfiguration
var config: CoreConfiguration = CoreConfiguration.new()
# Set logger level to DIAG
# Allow diagnostic and verbose messages
config.logger_level = CoreTypes.LoggerLevel.DIAG
# Initialize CORE with custom configuration
core = Core.new(config)
# Initialize CORE with standard configuration
# Initialize CORE with standard configuration, commented out
#core = Core.new()
# Initialization phase
func _ready() -> void:
# Initialize CORE completely
await get_tree().process_frame # to avoid "setting up nodes" error

View file

@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 0
---
# Setting up
@ -11,6 +11,20 @@ import TabItem from '@theme/TabItem';
This helps you install the CORE Framework in the first place.
## Available branches
You can choose one of the following branches to install CORE with:
<Tabs groupId="explanation">
<TabItem value="develop" label="develop">
The **develop** branch features the latest commits to the framework but contains experimental and breaking changes.
It should never be used in production, not even in testing *unless* you know what you're doing or directly contributing to the framework.
</TabItem>
<TabItem value="stable" label="stable">
The **stable** branch features the latest update to the framework and does not break often. Alphas, betas and release candidates will not show up on the stable branch, only releases will.
This branch of the framework should always be used in production and testing as it doesn't break without a framework-breaking bug or you not being able to read the changelog correctly.
</TabItem>
</Tabs>
## Using the Godot Asset Library
:::note
This will install the CORE Framework without the ability to easily download updates. You will need to look for updates yourself and install the latest version from the Asset Library yourself. If you don't want that hassle, use a [git installation](#using-git) instead.
@ -92,7 +106,10 @@ git commit -m "Update CORE Framework"
</Tabs>
## Changing branches
:::warning
Changing branches can result in errors and missing features. We don't accept issues regarding switching branches, you will be on your own!
Changing branches is not supported by the CORE Framework maintainers and contributors.
Doing so anyway will result in missing features, potential refactoring work and you need to manually review every single commit since the last release (if coming from the stable branch). \
\
**Again, we don't recommend switching branches. Doing so anyway may lead to issues!**
:::
<Tabs groupId="git-branch">
<TabItem value="direct-develop2stable" label="Develop » Stable">

View file

@ -1,8 +1,8 @@
{
"label": "Reference",
"position": 4,
"position": 3,
"link": {
"type": "generated-index",
"description": "CORE Framework module documentation"
"description": "CORE Framework reference"
}
}

View file

@ -6,6 +6,7 @@ description: Initializes and manages the framework.
# `CORE`
The **CORE Object** is responsible for initializing, managing and serving the CORE Framework.
## Versioning
### *int* <u>version_release</u>
CORE's version number.
@ -27,42 +28,42 @@ Use these to access CORE's modules.
## Variables
### *String* <u>basepath</u>
::danger[Don't modify]
:::danger[Don't modify]
Do not modify this.
::
:::
Stores the path to CORE's installation directory.
### *Dictionary* <u>custom_modules</u> = *{}*
### *void* <u>_ready</u>()
::danger[Don't modify]
:::danger[Don't modify]
Do not modify this.
::
:::
Contains a list of all loaded custom modules.
### *Node* <u>custom_modules_node</u>
::danger[Don't modify]
:::danger[Don't modify]
Do not modify this.
::
:::
Contains the node holding all custom modules as children.
## Functions
### *void* <u>_init</u>(*CoreConfiguration* <u>new_config</u>)
::danger[Don't call]
:::danger[Don't call]
Do not call this (except you're using `Core.new()`).
::
:::
Handles the preinitialization part. Does stuff like checking the engine version, loading the config and loading all modules into memory.
### *void* <u>_ready</u>()
::danger[Don't call]
:::danger[Don't call]
Do not call this.
::
:::
Handles the initialization part. Injects the builtin modules into the SceneTree and makes sure custom modules can be loaded properly. \
### *void* <u>initialize_modules</u>()
::danger[Don't call]
:::danger[Don't call]
Do not call this.
::
:::
Initializes all built-in modules during the preinitialization phase.
### *void* <u>inject_modules</u>()
::danger[Don't call]
:::danger[Don't call]
Do not call this.
::
:::
Injects CORE's built-in modules into the SceneTree.
### *void* <u>complete_init</u>(*bool* <u>no_success_message</u> = *false*)
Waits for all built-in and custom modules to fully initialize. \
@ -79,9 +80,9 @@ Please note that you can't get CORE's built-in modules with this function.
### *void* <u>reload_configuration</u>(*CoreConfiguration* <u>new_config</u> = *CoreConfiguration.new()*)
Loads a (new) configuration object and applies it to all modules.
### *void* <u>apply_configuration</u>()
::danger[Don't call]
:::danger[Don't call]
Do not call this.
::
:::
Applies the a configuration.
### *void* <u>cleanup</u>()
Makes sure that CORE does not leak memory on shutdown/unload. \
@ -110,7 +111,7 @@ You can use the following placeholders:
### *Array[int]* <u>get_version_semantic</u>()
Returns the CORE version in the semantic versioning scheme. The first integer contains the version number, the second integer contains the version type (`0` for alpha, `1` for beta, `2` for rc and `3` for release) and the last integer contains the version type number.
### *bool* <u>determine_basepath</u>()
::danger[Don't call]
:::danger[Don't call]
Do not call this.
::
:::
Determines CORE's installation/base path.

View file

@ -7,6 +7,7 @@ description: Template for CORE modules
Provides a basic template and a common foundation for building CORE modules. \
It provides common functions and variables used in all CORE modules.
## Variables
### *Core* <u>core</u>
Contains a reference to the [CORE Object](/reference/core).

View file

@ -1,5 +1,6 @@
---
sidebar_position: 1
description: The framework configuration
---
# `CoreConfiguration`
@ -7,6 +8,7 @@ Provides the default configuration for the CORE Framework.
## Note
All settings are variables.
## Global
### *bool* <u>headless</u> = *false*
Controls CORE's functionality. Renders GUI-related modules useless when set to `false`, which is the recommended behaviour on servers. For CORE's full functionality, set this to `true`.
@ -20,32 +22,24 @@ Allows or disallows custom modules.
## Logger
### *CoreTypes.LoggerLevel* <u>logger_level</u> = *CoreTypes.LoggerLevel.INFO*
I don't have to explain this, do I?
The minimum log level you want to be displayed.
### *bool* <u>logger_colored</u> = *true*
Toggles colored output. Set to `false` if you don't want that.
Determines if the logger's output should be colored.
### *String* <u>logger_format</u> = *"%color%[%time%] [%level% %source%:%line%] %message%"*
The format string the logger will operate on.
The template for all log messages
Available placeholders are: `%time%`, `%time_ms%`, `%level%`, `%color%`, `%message%`, `%source%`, `%source_raw%`, `%function%` and `%line%`
### *bool* <u>logger_newlines_override</u> = *true*
This example should make it clear, what this does:
```plain
logger_newlines_override = true:
[09:47:00] [INFO Test.gd:69] This is a test message...
with a newline!
logger_newlines_override = false:
[09:47:00] [INFO Test.gd:69] This is a test message...
with a newline!
```
Determines if identation should be provided if the logger encounters a newline.
### *int* <u>logger_newlines_sizelimit</u> = *40*
The maximum amount of characters than can appear before `%message%` before newlines won't be overriden. Setting this variable to `-1` disables this behaviour.
The maximum amount of characters excluding the message before newlines are no longer idented. Set to `-1` to disable this behaviour.
## Log UI
### *bool* <u>logui_enabled</u> = *true*
Determines if [`LogUI`](/terminology#logui) should be visible or not.
Enables or disables [Log UI](/terminology#logui).
### *Color* <u>logui_background_color</u> = *Color.BLACK*
The color the `LogUI` background will have. Set to `Color.TRANSPARENT` for a transparent background.
The Log UI background color.. Set to `Color.TRANSPARENT` for a transparent background.
### *int* <u>logui_font_size</u> = *14*
What size the graphical log should have.
What font size the graphical log should have.
## Easy Request Maker
### *CoreTypes.BlockadeLevel* <u>erm_unsecure_requests</u> = *CoreTypes.BlockadeLevel.BLOCK*

View file

@ -6,6 +6,7 @@ description: Contains various enums.
# `CoreTypes`
Contains globaly accessible custom enums and types used throughout the CORE Framework's source code.
## Enums
### <u>VersionType</u>{ RELEASE, RELEASECANDIDATE, BETA, ALPHA }
Available version types, following the StarOpenSource Versioning Specification (SOSVS) version 1.

View file

@ -1,9 +1,30 @@
---
sidebar_position: 8
description: Allows for awaited, batched and oneline requests.
---
# `Easy Request Maker`
Allows for awaited, batched and oneline requests.
Makes it easy to download files off the internet and communicate with HTTP servers.
## Variables
### *Dictionary* <u>list_queue</u> = *{}*
:::danger[Don't modify]
Do not modify this.
:::
Contains a list of all queued downloads.
### *Dictionary* <u>list_active</u> = *{}*
:::danger[Don't modify]
Do not modify this.
:::
Contains a list of all active downloads.
### *Dictionary* <u>list_complete</u> = *{}*
:::danger[Don't modify]
Do not modify this.
:::
Contains a list of all completed downloads.
## Functions
### *Dictionary* <u>awaited_request</u>(*String* <u>url</u>, *bool* <u>parse_utf8</u>, *HTTPClient.Method* <u>method</u> = *HTTPClient.Method.METHOD_GET*, *PackedStringArray* <u>headers</u> = *PackedStringArray([])*, *String* <u>data</u> = *""*)
@ -27,7 +48,7 @@ The returned `Dictionary` has the following structure (example):
:::note[Awaiting required]
Using the `await` keyword is required for this function.
:::
Returns a file from the internet without returning the godot code, http code or headers. Useful for oneliners.
Requests a file from the internet without returning the godot code, http code or headers. Useful for oneliners.
Returns `null` on error. To ignore HTTP errors (ie. non-200 statuses) set `ignore_http_code` to `true`.
Returns a UTF-8 string with `return_utf8` turned on, returns bytes when turned off.
@ -49,3 +70,29 @@ The returned `Dictionary`s have the following structure (example):
| ---------------------------------------------------------------------- The HTTP response code
------------------------------------------------------------------------------------ Equal to @GlobalScope.Error. If not 0/Error.OK = the request failed
```
### *int* <u>generate_id</u>()
:::danger[Don't call]
Do not call this.
:::
Returns a new download id.
### *int* <u>create_request</u>(*String* <url>, *HTTPClient.Method* <u>method</u> = *HTTPClient.Method.METHOD_GET*, *PackedStringArray* <u>headers</u> = *PackedStringArray([])*, *String* <u>body</u> = *""*)
:::warning
You'll probably not need this. Only use this function when implementing your own downloading method.
:::
Creates a new request and stores it in the queue. Returns the download id.
### *void* <u>start_request</u>(*int* <u>id</u>, *bool* <u>parse_utf8</u>)
:::note[Awaiting required]
Using the `await` keyword is required for this function.
:::
:::warning
You'll probably not need this. Only use this function when implementing your own downloading method.
:::
Configures and starts a queued request.
### *bool* <u>is_url_allowed</u>(*String* <url>)
Checks if `url` can be used.
### *bool* <u>is_request_completed</u>(*int* <u>id</u>)
Returns if a request has completed yet.
### *void* <u>clean_queue</u>()
Cleans the request queue.
### *void* <u>clean_completed</u>()
Cleans the completed requests list.

View file

@ -1,9 +1,11 @@
---
sidebar_position: 4
description: Your usual basic logger implementation, with some extra features.
---
# `Logger`
Prints formatted strings into the console/log.
Allows for colored output, better newlines, multiple logger levels and a large variety of placeholders usable in 'config_format'.
## Signals
### <u>log_event</u>
@ -14,25 +16,30 @@ Emitted on any log call, permitted or not. \
**format** is set to `""` when **allowed** is set `false`.
## Functions
### *CoreLoggerInstance* <u>get_instance</u>(*String* <u>origin</u>)
Returns a [logger instance](/reference/loggerinstance), which are two fancy words meaning you don't need to pass <u>origin</u> every time you want to log something.
### *bool* <u>is_level_allowed</u>(*CoreTypes.LoggerLevel* <u>level</u>)
Checks if the specified log level is permitted by the current configuration.
### *void* <u>_log</u>(*CoreTypes.LoggerLevel* <u>level</u>, *String* <u>origin</u>, *String* <u>message</u>)
:::danger[Don't call]
Do not call this.
:::
The main logging function that does the heavy lifting.
### *void* <u>diag</u>(*String* <u>origin</u>, *String* <u>message</u>)
Prints a diagnostic message
Prints a diagnostic message.
### *void* <u>verb</u>(*String* <u>origin</u>, *String* <u>message</u>)
Prints a verbose message
Prints a verbose message.
### *void* <u>info</u>(*String* <u>origin</u>, *String* <u>message</u>)
Prints an informational message
Prints an informational message.
### *void* <u>warn</u>(*String* <u>origin</u>, *String* <u>message</u>)
Prints a warning message
Prints a warning message.
### *void* <u>error</u>(*String* <u>origin</u>, *String* <u>message</u>)
Prints an error message
Prints an error message.
### *void* <u>crash</u>(*String* <u>origin</u>, *String* <u>message</u>, *bool* <u>framework_crash</u> = *false*)
:::note[Awaiting required]
Using the `await` keyword is required for this function.
:::
:::danger
Please do not set `framework_crash` to `true`. Thank you.
Don't set `framework_crash` to `true`, thanks!
:::
Handles crashes. Will terminate your game/application immediately.
### *bool* <u>is_level_allowed</u>(*CoreTypes.LoggerLevel* <u>level</u>)
Checks if the specified log level is allowed by the current configuration.
### *CoreLoggerInstance* <u>get_instance</u>(*String* <u>origin</u>)
Returns a [CoreLoggerInstance](/reference/loggerinstance), which is a fancy word meaning you don't need to pass `origin` every time you want to log something.

View file

@ -4,10 +4,8 @@ description: Passes `origin` for you.
---
# `LoggerInstance`
Pretty much a wrapper around CORE's logging implementation. \
CoreLoggerInstance's only job is to save you some effort aka. \
you not needing to pass the `origin` argument to each \
and every log call, which is extremely annoying. Thank us later ;)
Pretty much a wrapper around CORE's logging implementation. CoreLoggerInstance's only job is to save you some effort aka. you not needing to pass the `origin` argument to each and every log call, which is extremely annoying. Thank us later ;)
## Variables
### *CoreBaseModule* <u>logger</u>

View file

@ -1,18 +1,21 @@
---
sidebar_position: 6
description: Contains various utility functions.
---
# `Miscellaneous`
Miscellaneous functions that don't fit into other modules.
This module contains many methods that don't fit into any other module and generally make your life as a developer easier.
## Functions
### *void* <u>quit_safely</u>(*int* <u>exitcode</u> = *0*)
:::note[Awaiting required]
Using the `await` keyword is required for this function.
:::
Adds an small extra delay before exiting. Highly recommended over calling `get_tree().quit()` yourself.
Makes sure for all log messages to be flushed and that CORE is correctly cleaned up.
Using `get_tree().quit()` directly may cause various issues.
### *float* <u>byte2mib</u>(*int* <u>bytes</u>, *bool* flatten = *true*)
Converts a number of bytes to mebibytes.
Converts a number of bytes into mebibytes.
If `flatten` is set to `true`, the decimal part will be discarded.
### *float* <u>mib2byte</u>(*float* <u>mib</u>, *bool* <u>flatten</u> = *true*)
@ -30,7 +33,7 @@ If `flatten` is set to `true`, the decimal part will be discarded.
### *String* <u>format_stringarray</u>(*Array[String]* <u>array</u>, *String* <u>item_before</u> = *""*, *String* <u>item_after</u> = *""*, *String* <u>separator_list</u> = *", "*, *String* <u>separator_final</u> = *" & "*)
Converts a string array into a normal, nicely formatted string.
With `item_before` and `item_after` you can customize the lists appearance. Here's a example on how to format every item bold:
With `item_before` and `item_after` you can customize the lists appearance. Here's an example on how to format every item bold:
```gdscript
extends Node

View file

@ -1,34 +1,66 @@
---
sidebar_position: 7
description: Manages scenes for you efficiently.
---
# `Scene Management System`
Handles scenes and their order.
Allows for organized scene management, making development much faster.
## Constants
## *Array[String]* <u>scene_nodes</u> = *[ "debug", "cutscene", "menu", "main", "background" ]*
Used internally for adding, managing and removing scene collections.
## Variables
## *Node* <u>scenes_debug</u> = *Node.new()*
:::danger[Don't modify]
Do not modify this.
:::
The 'debug' scene collection.
## *Node* <u>scenes_cutscene</u> = *Node.new()*
:::danger[Don't modify]
Do not modify this.
:::
The 'cutscene' scene collection.
## *Node* <u>scenes_menu</u> = *Node.new()*
:::danger[Don't modify]
Do not modify this.
:::
The 'menu' scene collection.
## *Node* <u>scenes_main</u> = *Node.new()*
:::danger[Don't modify]
Do not modify this.
:::
The 'main' scene collection.
## *Node* <u>scenes_background</u> = *Node.new()*
:::danger[Don't modify]
Do not modify this.
:::
The 'background' scene collection.
## *Dictionary* <u>scenes</u> = *{}*
:::danger[Don't modify]
Do not modify this.
:::
A list of all loaded scenes
## Functions
### *bool* <u>add_scene</u>(*String* <u>sname</u>, *Node* <u>sclass</u>, *CoreTypes.SceneType* <u>type</u>)
Adds a scene to some scene collection.
Returns `true` if successful.
### *bool* <u>remove_scene</u>(*String* <u>sname</u>, *bool* <u>force_remove</u> = *false*)
:::danger
Please do not set `force_remove` to `true`. Thank you.
Don't set `force_remove` to `true`, thanks!
:::
Removes a scene from some scene collection.
Returns `true` if successful.
### *Node* <u>get_scene</u>(*String* <u>sname</u>)
Returns a scene from some scene collection.
Returns a scene from some scene collection. \
\
Returns `null` if no scene with that name was found.
### *Node* <u>get_scene_collection</u>(*CoreTypes.SceneType* <u>type</u>)
:::danger
NEVER change a scene collection's properties or free it! You can CRASH the CORE Framework easily by doing this!
Don't change any properties of the scene collection or free it, otherwise you may cause breakage.
:::
:::danger
NEVER free a scene yourself! You can CRASH the CORE Framework easily by doing this!
:::
Returns a scene collection node. Useful if you want to change the node's position or want direct access to one.
Returns a scene collection node. \
Useful if you want to change a child's index.
### *Array[Node]* <u>get_scene_collection_list</u>(*CoreTypes.SceneType* <u>type</u>)
Returns a list of all loaded scenes in some scene collection.
### *int* <u>get_scene_collection_count</u>(*CoreTypes.SceneType* <u>type</u>)
@ -36,4 +68,4 @@ Returns the number of loaded scenes in some scene collection.
### *CoresTypes.SceneType* <u>exists</u>(*String* <u>sname</u>)
Returns the scene collection a scene is loaded in.
Returns `CoreTypes.SceneType.NONE` if no scene with that name has been found.
Returns `CoreTypes.SceneType.NONE` if no scene with that name was found.

View file

@ -1,20 +1,32 @@
---
sidebar_position: 9
description: Easy config management.
---
# `Storage`
Allows you to write configuration files with ease, without any headaches.
## Variables
### *bool* <u>is_open</u>
:::danger[Do not modify]
NEVER change the value of this variable. You will break stuff with this!
### *bool* <u>is_open</u> = *false*
:::danger[Don't modify]
Do not modify this.
:::
Indicates if a storage file is currently open.
### *Dictionary* <u>storage</u> = *{}*
:::danger[Don't modify]
Do not modify this.
:::
The parsed data inside the storage file.
### *String* <u>storage_location</u> = *""*
:::danger[Don't modify]
Do not modify this.
:::
The location of the storage file.
## Functions
### *bool* <u>open_storage</u>(*String* <u>location</u>, *bool* <u>create_new</u> = *true*, *bool* <u>sanity_check</U> = *true*, *bool* <u>fail_on_sanity_check</u> = *false*)
Loads a storage file from `location` and creates a new one first if missing (`create_new`). Also performs basic sanity checking (`sanity_check`) and may throw an error on failure (`fail_on_sanity_check`).
Loads a storage file into memory.
### *bool* <u>close_storage</u>()
Closes the active storage file.
### *bool* <u>save_storage</u>()
@ -28,8 +40,10 @@ Updates a storage key with the specified value.
### *bool* <u>del_key</u>(*String* <u>key</u>, *bool* <u>autosave</u> = *true*)
Deletes a storage key.
### *Dictionary* <u>get_dict</u>()
Returns the `storage` dictionary, useful for more advanced operations. Changes are not reflected onto the `storage` dictionary though. To save changes through this module, pass your modified dictionary to [`save_dict`](#bool-save_dictdictionary-dict-bool-sanity_check--true-bool-fail_on_sanity_check--false-bool-autosave--true).
Returns the `storage` `Dictionary`, useful for more advanced operations. \
Changes are not reflected onto the `storage` dictionary though. To save changes through this module, \
pass your modified dictionary to `save_dict`.
### *bool* <u>save_dict</u>(*Dictionary* <u>dict</u>, *bool* <u>sanity_check</u> = *true*, *bool* <u>fail_on_sanity_check</u> = *false*, *bool* <u>autosave</u> = *true*)
Saves a arbitrary dictionary as a `storage` dictionary with sanity checking (`sanity_check` and `fail_on_sanity_check`).
### *Array[String]* <u>perform_sanity_check</u>(*Dictionary* <u>storage_check</u>)
Performs sanity checks on a dictionary aka. checks the type of all keys and their values.
Performs sanity checks on a [class Dictionary] to determine if it can be saved and loaded using this module.

View file

@ -1,17 +1,25 @@
---
sidebar_position: 2
sidebar_position: 1
description: You don't know a word? Look it up here.
---
# Terminology
You don't know a word? Look it up here.
## CORE Object aka. `CORE`
The **CORE Object** is the class you use to initialize the CORE Framework.
## `Log UI`
Displays the log/console output graphically in the background.
## `ERM`
The **CORE Object** is the class you use to initialize the framework. It holds all modules and initializes and manages the framework.
## Log UI
Displays the log/console output shown graphically in the background.
## ERM
Stands for **Easy Request Maker** and allows you to download stuff.
## `EDL`
The old name for the [`ERM`](#erm) module, which was called `Easy DownLoader` previously.
## `SMS`
## EDL
The old name for the [`ERM`](#erm) module. Stood for `Easy DownLoader`.
## SMS
No, it does not stand for **Short Message Service**, but for **Scene Management System**. It manages your scenes.
## CMS
Stands for **Custom Module Support**.

View file

@ -15,8 +15,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
## Template for CORE modules.[br]
## [br]
## Template for CORE modules.
##
## Provides a basic template and a common foundation for building CORE modules.[br]
## It provides common functions and variables used in all CORE modules.
extends Node

View file

@ -2,8 +2,9 @@
# Copyright (c) 2024 The StarOpenSource Project & Contributors
# Licensed in the Public Domain
## The [code]CoreConfiguration[/code] class holds the Framework's settings.[br]
## The default configuration is designed to be usable without any modification.
## The framework configuration
##
## Provides the default configuration for the CORE Framework.
extends Node
class_name CoreConfiguration
@ -18,30 +19,23 @@ class_name CoreConfiguration
## Allows or disallows custom modules.
@export var custom_modules: bool
@export_category("Logger")
## I don't have to explain this, do I?
## The minimum log level you want to be displayed.
@export var logger_level: CoreTypes.LoggerLevel
## Toggles colored output. Set to [code]false[/code] if you don't want that.
## Determines if the logger's output should be colored.
@export var logger_colored: bool
## The format string the logger will operate on. Available placeholders are: [code]%time%[/code], [code]%time_ms%[/code], [code]%level%[/code], [code]%color%[/code], [code]%message%[/code], [code]%source%[/code], [code]%source_raw%[/code], [code]%function%[/code] and [code]%line%[/code]
## The template for all log messages.[br]
## Available placeholders are: [code]%time%[/code], [code]%time_ms%[/code], [code]%level%[/code], [code]%color%[/code], [code]%message%[/code], [code]%source%[/code], [code]%source_raw%[/code], [code]%function%[/code] and [code]%line%[/code]
@export var logger_format: String
## This example should make it clear, what this does:[br]
## [codeblock]
## logger_newlines_override = true:
## [09:47:00] [INFO Test.gd:69] This is a test message...
## with a newline!
## logger_newlines_override = false:
## [09:47:00] [INFO Test.gd:69] This is a test message...
## with a newline!
## [/codeblock]
## Determines if identation should be provided if the logger encounters a newline.
@export var logger_newlines_override: bool
## The maximum amount of characters than can appear before [code]%message%[/code] before newlines won't be overriden. Setting this variable to [code]-1[/code] disables this behaviour.
## The maximum amount of characters excluding the message before newlines are no longer idented. Set to [code]-1[/code] to disable this behaviour.
@export var logger_newlines_sizelimit: int
@export_category("Log UI")
## Determines if [code]LogUI[/code] should be visible or not.
## Enables or disables Log UI.
@export var logui_enabled: bool
## The color the [code]LogUI[/code] background will have. Set to [code]Color.TRANSPARENT[/code] for a transparent background.
## The Log UI background color. Set to [code]Color.TRANSPARENT[/code] for a transparent background.
@export var logui_background_color: Color
## What size the graphical log should have.
## What font size the graphical log should have.
@export var logui_font_size: int
@export_category("Easy Request Maker")
## Determines how unsecure requests should be handled.

View file

@ -15,11 +15,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
## Passes [code]origin[/code] for you.[br]
## [br]
## Pretty much a wrapper around CORE's logging implementation.[br]
## CoreLoggerInstance's only job is to save you some effort aka.[br]
## you not needing to pass the [code]origin[/code] argument to each[br]
## Passes [code]origin[/code] for you.
##
## Pretty much a wrapper around CORE's logging implementation.
## CoreLoggerInstance's only job is to save you some effort aka.
## you not needing to pass the [code]origin[/code] argument to each
## and every log call, which is extremely annoying. Thank us later ;)
extends Node
class_name CoreLoggerInstance
@ -38,12 +38,12 @@ func _init(logger_new: CoreBaseModule, origin_new: String) -> void:
func diag(message: String) -> void: logger.diag(origin, message)
## Prints a verbose message.
func verb(message: String) -> void: logger.verb(origin, message)
## Prints a informational message.
## Prints an informational message.
func info(message: String) -> void: logger.info(origin, message)
## Prints a warning message.
func warn(message: String) -> void: logger.warn(origin, message)
## Prints a error message.
## Prints an error message.
func error(message: String) -> void: logger.error(origin, message)
## Handles crashes. Will terminate your game/application immediately.
## Note: Awaiting required.
## Note: Using the [code]await[/code] keyword is required for this function.
func crash(message: String) -> void: await logger.crash(origin, message)

View file

@ -15,7 +15,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
## Contains enums and types shared across the CORE Framework.
## Contains various enums.
##
## Contains globaly accessible custom enums and types used throughout the CORE Framework's source code.
extends Node
class_name CoreTypes

View file

@ -18,11 +18,20 @@
## Allows for awaited, batched and oneline requests.
extends CoreBaseModule
## Contains a list of all queued downloads.[br]
## Danger: Do not modify this.
var list_queue: Dictionary = {}
## Contains a list of all active downloads.[br]
## Danger: Do not modify this.
var list_active: Dictionary = {}
## Contains a liust of all completed downloads.[br]
## Danger: Do not modify this.
var list_complete: Dictionary = {}
## Determines how unsecure requests should be handled.
var config_unsecure_requests: CoreTypes.BlockadeLevel
# +++ module +++
func _cleanup() -> void:
clean_queue()
clean_completed()
@ -32,12 +41,21 @@ func _cleanup() -> void:
await get_tree().process_frame
remove_child(child)
func generate_id() -> int:
var id = randi()
if list_queue.has(id) or list_active.has(id): return generate_id()
logger.diagf("erm", "Generated new download id " + str(id))
return id
# +++ methods that do the heavily lifting +++
## Requests a file from the internet.[br]
## [br]
## The returned [code]Dictionary[/code] has the following structure (example):[br]
## [codeblock]
## { "result": 0, "http_code": 200, "headers": [ "Server": "nginx" ], "body": [], "body_utf8": [] }
## ^ ^ ^ ^ ^
## | | | | |
## | | | | --------- The body from the server, as a UTF-8 string (set to "" if 'parse_utf8' is false)
## | | | ----------------------- The body from the server, as bytes
## | | ------------------------------------------------------ A array of headers
## | ---------------------------------------------------------------------- The HTTP response code
## ------------------------------------------------------------------------------------ Equal to @GlobalScope.Error. If not 0/Error.OK = the request failed
## [/codeblock][br]
## Note: Using the [code]await[/code] keyword is required for this function.
func awaited_request(url: String, parse_utf8: bool, method: HTTPClient.Method = HTTPClient.Method.METHOD_GET, headers: PackedStringArray = PackedStringArray([]), data: String = "") -> Dictionary:
logger.verbf("erm", "Creating awaited request")
if !await is_url_allowed(url): return {}
@ -49,6 +67,11 @@ func awaited_request(url: String, parse_utf8: bool, method: HTTPClient.Method =
list_complete.erase(id)
return dldata
## Requests a file from the internet without returning the godot code, http code or headers. Useful for oneliners.[br]
## [br]
## Returns [code]null[/code] on error. To ignore HTTP errors (ie. non-200 statuses) set [code]ignore_http_code[/code] to [code]true[/code].[br]
## Returns a UTF-8 string with [code]return_utf8[/code] turned on, returns bytes when turned off.[br]
## Note: Using the [code]await[/code] keyword is required for this function.
func oneline_awaited_request(url: String, return_utf8: bool = true, ignore_http_code: bool = false, method: HTTPClient.Method = HTTPClient.Method.METHOD_GET, headers: PackedStringArray = PackedStringArray([]), data: String = "") -> Variant:
var dldata: Dictionary = await awaited_request(url, return_utf8, method, headers, data)
if dldata == {}: return null
@ -58,6 +81,21 @@ func oneline_awaited_request(url: String, return_utf8: bool = true, ignore_http_
if return_utf8: return dldata["body_utf8"]
else: return dldata["body"]
## Requests multiple files from the internet.[br]
## [br]
## Thee returned [code]Dictionary[/code]s have the following structure (example):
## [codeblock]
## { "result": 0, "http_code": 200, "headers": [ "Server": "nginx" ], "body": [], "body_utf8": [] }
## ^ ^ ^ ^ ^
## | | | | |
## | | | | --------- The body from the server, as a UTF-8 string (set to "" if 'parse_utf8' is false)
## | | | ----------------------- The body from the server, as bytes
## | | ------------------------------------------------------ A array of headers
## | ---------------------------------------------------------------------- The HTTP response code
## ------------------------------------------------------------------------------------ Equal to @GlobalScope.Error. If not 0/Error.OK = the request failed
## [/codeblock][br]
## Note: Using the [code]await[/code] keyword is required for this function.
func batch_awaited_request(urls: PackedStringArray, parse_utf8: bool, method: HTTPClient.Method = HTTPClient.Method.METHOD_GET, headers: PackedStringArray = PackedStringArray([]), data: String = "") -> Array[Dictionary]:
logger.verbf("erm", "Creating " + str(urls.size()) + " awaited request(s)")
var dldata: Array[Dictionary] = []
@ -76,11 +114,29 @@ func _batch_awaited_request(url: String, parse_utf8: bool, method: HTTPClient.Me
logger.diagf("erm", "Waiting for request " + str(id) + " to finish")
while !is_request_completed(id): await get_tree().create_timer(0.1, true).timeout
return id
# +++ internal +++
## Returns a new download id.[br]
## Danger: Don't call this.
func generate_id() -> int:
var id = randi()
if list_queue.has(id) or list_active.has(id):
logger.warnf("erm", "New download id '" + str(id) + "' is already taken")
return generate_id()
logger.diagf("erm", "Generated new download id " + str(id))
return id
## Creates a new request and stores it in the queue. Returns the download id.[br]
## Warning: You'll probably not need this. Only use this function when implementing your own downloading method.
func create_request(url: String, method: HTTPClient.Method = HTTPClient.Method.METHOD_GET, headers: PackedStringArray = PackedStringArray([]), body: String = "") -> int:
logger.verbf("erm", "Creating new request\n-> URL: " + url + "\n-> Method: " + str(method) + "\nHeaders: " + str(headers.size()) + "\nBody size: " + str(body.length()) + " Characters")
var id = generate_id()
list_queue.merge({ id: { "url": url, "method": method, "headers": headers, "body": body } })
return id
## Configures and starts a queued request.[br]
## Note: Using the [code]await[/code] keyword is required for this function.[br]
## Warning: You'll probably not need this. Only use this function when implementing your own downloading method.
func start_request(id: int, parse_utf8: bool) -> void:
logger.verbf("erm", "Starting request " + str(id))
list_active.merge({ id: list_queue[id] })
@ -100,12 +156,31 @@ func start_request(id: int, parse_utf8: bool) -> void:
add_child(download)
download.request(list_active[id]["url"], list_active[id]["headers"], list_active[id]["method"], list_active[id]["body"])
## Checks if [code]url[/code] can be used.
func is_url_allowed(url: String) -> bool:
if url.begins_with("http://"):
match(config_unsecure_requests):
CoreTypes.BlockadeLevel.BLOCK:
logger.errorf("erm", "Blocked unsecure url '" + url + "'")
return false
CoreTypes.BlockadeLevel.WARN: logger.warnf("erm", "Requesting unsecure url '" + url + "'")
CoreTypes.BlockadeLevel.IGNORE: pass
_: await logger.crashf("erm", "Invalid BlockadeLevel '" + str(config_unsecure_requests) + "'")
elif url.begins_with("https://"): pass
else:
logger.errorf("erm", "Invalid url '" + url + "'")
return false
return true
## Returns if a request has completed yet.
func is_request_completed(id: int) -> bool: return list_complete.has(id)
## Cleans the request queue.
func clean_queue() -> void:
logger.verbf("erm", "Cleaning request queue")
list_queue.clear()
## Cleans the completed requests list.
func clean_completed() -> void:
logger.verbf("erm", "Cleaning completed requests")
list_complete.clear()

View file

@ -21,17 +21,21 @@
## large variety of placeholders usable in [param config_format].
extends CoreBaseModule
# Signals
signal log_event
# Configuration
## The minimum log level you want to be displayed.
var config_level: CoreTypes.LoggerLevel
## Determines if the logger's output should be colored.
var config_colored: bool
## The template for all log messages[br]
## Available placeholders are: [code]%time%[/code], [code]%time_ms%[/code], [code]%level%[/code], [code]%color%[/code], [code]%message%[/code], [code]%source%[/code], [code]%source_raw%[/code], [code]%function%[/code] and [code]%line%[/code]
var config_format: String
## Determines if identation should be provided if the logger encounters a newline.
var config_newlines_override: bool
## The maximum amount of characters excluding the message before newlines are no longer idented. Set to [code]-1[/code] to disable this behaviour.
var config_newlines_sizelimit: int
# Update configuration
# +++ module +++
func _pull_config() -> void:
config_level = core.config.logger_level
config_colored = core.config.logger_colored
@ -39,7 +43,9 @@ func _pull_config() -> void:
config_newlines_override = core.config.logger_newlines_override
config_newlines_sizelimit = core.config.logger_newlines_sizelimit
# Creates log messages
# +++ logging +++
## The main logging function that does the heavy lifting.[br]
## Danger: Do not call.
func _log(level: CoreTypes.LoggerLevel, origin: String, message: String) -> void:
if !is_level_allowed(level):
emit_signal("log_event", false, level, origin, message, "")
@ -82,19 +88,20 @@ func _log(level: CoreTypes.LoggerLevel, origin: String, message: String) -> void
if config_colored: print_rich(format)
else: print(format)
# Check if level is allowed
func is_level_allowed(level: CoreTypes.LoggerLevel) -> bool:
if level <= config_level: return true
else: return false
# Self explanitory
# +++ self explanitory +++
## Prints a diagnostic log message.
func diag(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.DIAG, origin, message)
## Prints a verbose log message.
func verb(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.VERB, origin, message)
## Prints an informational log message.
func info(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.INFO, origin, message)
## Prints a warning log message.
func warn(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.WARN, origin, message)
## Prints an error log message.
func error(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.ERROR, origin, message)
# Built-in crash handler for CORE and applications using it
## Handles crashes. Will terminate your game/application immediately.
## Note: Using the [code]await[/code] keyword is required for this function.
## Danger: Don't set [code]framework_crash[/code] to [code]true[/code], thanks!
func crash(origin: String, message: String, framework_crash: bool = false) -> void:
# Collect information
var stack: Array[Dictionary] = get_stack()
@ -186,13 +193,31 @@ STACKTRACE
# Shutdown
await core.misc.quit_safely(69)
# Makes CORE development easier
# Deprecated functions, will be removed soon
## Deprecated framework call
## @deprecated
func diagf(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.DIAG, core.basepath.replace("res://", "") + "src/" + origin + ".gd", message)
## Deprecated framework call
## @deprecated
func verbf(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.VERB, core.basepath.replace("res://", "") + "src/" + origin + ".gd", message)
## Deprecated framework call
## @deprecated
func infof(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.INFO, core.basepath.replace("res://", "") + "src/" + origin + ".gd", message)
## Deprecated framework call
## @deprecated
func warnf(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.WARN, core.basepath.replace("res://", "") + "src/" + origin + ".gd", message)
## Deprecated framework call
## @deprecated
func errorf(origin: String, message: String) -> void: _log(CoreTypes.LoggerLevel.ERROR, core.basepath.replace("res://", "") + "src/" + origin + ".gd", message)
## Deprecated framework call
## @deprecated
func crashf(origin: String, message: String) -> void: crash(core.basepath.replace("res://", "") + "src/" + origin + ".gd", message)
# Returns a logger instance
# +++ etc +++
## Checks if the specified log level is allowed by the current configuration.
func is_level_allowed(level: CoreTypes.LoggerLevel) -> bool:
if level <= config_level: return true
else: return false
## Returns a [class CoreLoggerInstance], which is a fancy word meaning you don't need to pass [code]origin[/code] every time you want to log something.
func get_instance(origin: String) -> CoreLoggerInstance: return CoreLoggerInstance.new(self, origin)

View file

@ -28,6 +28,20 @@ var logrtl: RichTextLabel
var font_normal: Font
var font_bold: Font
# +++ module +++
func _pull_config() -> void:
background.visible = !core.config.headless and core.config.logui_enabled
background.color = core.config.logui_background_color
logrtl.add_theme_font_size_override("normal_font_size", core.config.logui_font_size)
logrtl.add_theme_font_size_override("bold_font_size", core.config.logui_font_size)
func _cleanup() -> void:
background.remove_child(logrtl)
core.sms.remove_child(background)
logrtl.queue_free()
background.queue_free()
# +++ initialization +++
func _initialize() -> void:
# Load fonts into memory
font_normal = load(core.basepath + "dist/FiraCode/Regular.ttf")
@ -78,18 +92,7 @@ func _ready() -> void:
# Connect log_event
logger.connect("log_event", func(allowed: bool, _level: CoreTypes.LoggerLevel, _origin: String, _message: String, format: String) -> void: if allowed: logrtl.text = logrtl.text + format + "\n")
func _cleanup() -> void:
background.remove_child(logrtl)
core.sms.remove_child(background)
logrtl.queue_free()
background.queue_free()
func _pull_config() -> void:
background.visible = !core.config.headless and core.config.logui_enabled
background.color = core.config.logui_background_color
logrtl.add_theme_font_size_override("normal_font_size", core.config.logui_font_size)
logrtl.add_theme_font_size_override("bold_font_size", core.config.logui_font_size)
# +++ process +++
func _process(_delta: float) -> void:
if !core.config.headless:
var window_size: Vector2i = DisplayServer.window_get_size()

View file

@ -15,36 +15,59 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
## Contains various useful functions that do not fit into other modules.
## Contains various utility functions.
##
## Contains various useful functions that you can use to save yourself some time
## programming or searching.
## This module contains many methods that don't fit into any other module
## and generally make your life as a developer easier.
extends CoreBaseModule
func quit_safely(exitcode: int = 0) -> void:
logger.infof("misc", "Shutting down (code " + str(exitcode) + ")")
logger.diagf("misc", "Waiting for log messages to be flushed")
await get_tree().create_timer(0.25).timeout
await core.cleanup()
get_tree().quit(exitcode)
# +++ data type conversion +++
## Converts a number of bytes into mebibytes.[br]
## [br]
## If [code]flatten[/code] is set to [code]true[/code], the decimal part will be discarded.
@warning_ignore("integer_division")
func byte2mib(bytes: int, flatten: bool = true) -> float:
if flatten: return bytes/1048576
return bytes/float(1048576)
## Converts a number of mebibytes into bytes.[br]
## [br]
## If [code]flatten[/code] is set to [code]true[/code], the decimal part will be discarded.
func mib2byte(mib: float, flatten: bool = true) -> float:
if flatten: return int(mib*1048576)
return mib*1048576
## Converts a number of mebibytes into gibibytes.[br]
## [br]
## If [code]flatten[/code] is set to [code]true[/code], the decimal part will be discarded.
func mib2gib(mib: float, flatten: bool = true) -> float:
if flatten: return int(mib/1024)
return mib/1024
## Converts a number of gebibytes into mebibytes.[br]
## [br]
## If [code]flatten[/code] is set to [code]true[/code], the decimal part will be discarded.
func gib2mib(gib: float, flatten: bool = true) -> float:
if flatten: return int(gib*1024)
return gib*1024
# +++ type formatting +++
## Converts a string array into a normal, nicely formatted string.[br]
## [br]
## With [code]item_before[/code] and [code]item_after[/code] you can customize the lists apperance. Here's an example on how to format every item bold:
## [codeblock]
## extends Node
##
## var core: Core = get_node("/root/CORE")
## var misc: CoreBaseModule = core.misc
## var logger: CoreLoggerInstance = core.logger.get_instance("some/script.gd")
##
## func _ready() -> void:
## var array: Array[String] = ["Apples", "Bananas", "Oranges"]
##
## logger.info(misc.format_stringarray(array))
## logger.info(misc.format_stringarray(array, "[b]", "[/b]"))
## [/codeblock]
func format_stringarray(array: Array[String], item_before: String = "", item_after: String = "", separator_list: String = ", ", separator_final: String = " & ") -> String:
var output: String = ""
@ -62,6 +85,10 @@ func format_stringarray(array: Array[String], item_before: String = "", item_aft
return output
# +++ array conversion +++
## Converts an array into a string array.[br]
## [br]
## If an item is found that is not of type [code]String[/code], an empty array is returned.
func array_to_stringarray(array: Array) -> Array[String]:
var output: Array[String] = []
@ -73,6 +100,7 @@ func array_to_stringarray(array: Array) -> Array[String]:
return output
## Converts a string array into an array.
func stringarray_to_array(array: Array[String]) -> Array:
var output: Array = []
@ -81,5 +109,27 @@ func stringarray_to_array(array: Array[String]) -> Array:
return output
# +++ etc +++
## Calculates the center of the child in the area of the parent.[br]
## [br]
## Example:
## [codeblock]
## extends Control
##
## var core: Core = get_node("/root/CORE")
## var misc: CoreBaseModule = core.misc
##
## func _ready() -> void:
## position = misc.center_object(get_parent().size, size)
## [/codeblock]
func get_center(parent_size: Vector2, child_size: Vector2) -> Vector2:
return Vector2(parent_size.x/2-child_size.x/2, parent_size.y/2-child_size.y/2)
## Makes sure for all log messages to be flushed and that CORE is correctly cleaned up.
## Using [method SceneTree.quit] directly may cause various issues.
## Note: Using the [code]await[/code] keyword is required for this function.
func quit_safely(exitcode: int = 0) -> void:
logger.infof("misc", "Shutting down (code " + str(exitcode) + ")")
await get_tree().create_timer(0.25).timeout
await core.cleanup()
get_tree().quit(exitcode)

View file

@ -15,22 +15,34 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
## Manages scenes more efficiently.
## Manages scenes for you efficiently.
##
## Allows for organized scene management, making development much faster.
extends CoreBaseModule
# Objects
## Used internally for adding, managing and removing scene collections.
const scene_nodes: Array[String] = [ "debug", "cutscene", "menu", "main", "background" ]
## The 'debug' scene collection.[br]
## Danger: Don't modify this.
var scenes_debug: Node = Node.new()
## The 'cutscene' scene collection.[br]
## Danger: Don't modify this.
var scenes_cutscene: Node = Node.new()
## The 'menu' scene collection.[br]
## Danger: Don't modify this.
var scenes_menu: Node = Node.new()
## The 'main' scene collection.[br]
## Danger: Don't modify this.
var scenes_main: Node = Node.new()
## The 'background' scene collection.[br]
## Danger: Don't modify this.
var scenes_background: Node = Node.new()
# Variables
## A list of all loaded scenes[br]
## Danger: Don't modify this.
var scenes: Dictionary = {}
# +++ module +++
func _initialize() -> void:
for scene in scene_nodes:
get("scenes_" + scene).name = scene.to_upper()
@ -50,7 +62,8 @@ func _pull_config() -> void:
if is_inside_tree(): logger.verbf("sms", "Removing all scenes (triggered by headless mode)")
for scene in scenes: remove_scene(scene, true)
# Add a scene to some scene collection
# +++ scene management +++
## Adds a scene to some scene collection.
func add_scene(sname: String, sclass: Node, type: CoreTypes.SceneType) -> bool:
if core.config.headless: return false
logger.verbf("sms", "Adding scene \"" + sname + "\" of type " + str(type))
@ -74,7 +87,8 @@ func add_scene(sname: String, sclass: Node, type: CoreTypes.SceneType) -> bool:
scenes.merge({ sname: { "type": type, "class": sclass } })
return true
# Remove a scene from some scene collection
## Removes a scene from some scene collection.
## Danger: Don't set [code]force_remove[/code] to [code]true[/code], thanks!
func remove_scene(sname: String, force_remove: bool = false) -> bool:
if core.config.headless and !force_remove: return false
if force_remove: await logger.crashf("sms", "force_remove = true is not allowed", true)
@ -102,7 +116,10 @@ func remove_scene(sname: String, force_remove: bool = false) -> bool:
scenes.erase(sname)
return true
# Return a loaded scene
# +++ getters +++
## Returns a scene from some scene collection.[br]
## [br]
## Returns [code]null[/code] if no scene with that name was found.
func get_scene(sname: String) -> Node:
if core.config.headless: return null
match(exists(sname)):
@ -115,7 +132,9 @@ func get_scene(sname: String) -> Node:
_: await logger.crashf("sms", "Invalid SceneType " + str(exists(sname)), true)
return null
# Return a scene collection for scene manipulation
## Returns a scene collection node.[br]
## Useful if you want to change a child's index.[br]
## Danger: Don't change any properties of the scene collection or free it, otherwise you may cause breakage.
func get_scene_collection(type: CoreTypes.SceneType) -> Node:
if core.config.headless: return null
match(type):
@ -128,7 +147,7 @@ func get_scene_collection(type: CoreTypes.SceneType) -> Node:
_: await logger.crashf("sms", "Invalid SceneType " + str(type), true)
return null
# Return scenes in some scene collection
## Returns a list of all loaded scenes in some scene collection.
func get_scene_collection_list(type: CoreTypes.SceneType) -> Array[Node]:
var list: Array[Node] = []
for scene in scenes:
@ -136,7 +155,7 @@ func get_scene_collection_list(type: CoreTypes.SceneType) -> Array[Node]:
list.append(scenes[scene]["class"])
return list
# Return scene count in some scene collection
## Returns the number of loaded scenes in some scene collection.
func get_scene_collection_count(type: CoreTypes.SceneType) -> int:
var amount: int = 0
for scene in scenes:
@ -144,7 +163,9 @@ func get_scene_collection_count(type: CoreTypes.SceneType) -> int:
amount += 1
return amount
# Return scene existance & scene collection
## Returns the scene collection a scene is loaded in.[br]
## [br]
## [enum CoreTypes.SceneType][code].NONE[/code] if no scene with that name was found.
func exists(sname: String) -> CoreTypes.SceneType:
for scene in scenes:
if scene == sname: return scenes[scene]["type"]

View file

@ -16,10 +16,22 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
extends CoreBaseModule
## Easy config management.
##
## Allows you to read and write configuration files with ease, without any headaches.
## Indicates if a storage file is currently open.
## Danger: Don't modify this.
var is_open: bool = false
## The parsed data inside the storage file.
## Danger: Don't modify this.
var storage: Dictionary = {}
## The location of the storage file.
## Danger: Don't modify this.
var storage_location: String = ""
# +++ file management +++
## Opens a storage file into memory.
func open_storage(location: String, create_new: bool = true, sanity_check: bool = true, fail_on_sanity_check: bool = false) -> bool:
if is_open:
logger.errorf("storage", "Failed to open storage: A storage file is already open")
@ -61,6 +73,7 @@ func open_storage(location: String, create_new: bool = true, sanity_check: bool
is_open = true
return true
## Closes the active storage file.
func close_storage() -> bool:
if !is_open:
logger.errorf("storage", "Failed to close storage: No storage file is open")
@ -70,6 +83,7 @@ func close_storage() -> bool:
is_open = false
return true
## Saves the active storage file to disk.
func save_storage() -> bool:
if !is_open:
logger.errorf("storage", "Failed to save storage: No storage file is open")
@ -83,6 +97,8 @@ func save_storage() -> bool:
file.close()
return true
# +++ config manipulation +++
## Removes all keys from the active storage file. The nuclear option basically.
func nuke_storage(autosave: bool = true) -> bool:
if !is_open:
logger.errorf("storage", "Failed to nuke storage: No storage file is open")
@ -92,6 +108,7 @@ func nuke_storage(autosave: bool = true) -> bool:
if autosave: save_storage()
return true
## Returns a storage key. Can also return a default value if unset.
func get_key(key: String, default: Variant = null) -> Variant:
if !is_open:
logger.errorf("storage", "Failed to get key: No storage file is open")
@ -99,6 +116,7 @@ func get_key(key: String, default: Variant = null) -> Variant:
logger.diagf("storage", "Returning storage key \"" + key + "\" (default='" + str(default) + "')")
return storage.get(key, default)
## Updates a storage key with the specified value.
func set_key(key: String, value: Variant, overwrite: bool = true, autosave: bool = true) -> bool:
if !is_open:
logger.errorf("storage", "Failed to set key: No storage file is open")
@ -108,6 +126,7 @@ func set_key(key: String, value: Variant, overwrite: bool = true, autosave: bool
if autosave: save_storage()
return true
## Deletes a storage key.
func del_key(key: String, autosave: bool = true) -> bool:
if !is_open:
logger.errof("storage", "Failed to delete key: No storage file is open")
@ -117,6 +136,9 @@ func del_key(key: String, autosave: bool = true) -> bool:
if autosave: save_storage()
return true
## Returns the [param storage] [class Dictionary], useful for more advanced operations.[br]
## Changes are not reflected onto the [param storage] though. To save changes through this module,[br]
## pass your modified [class Dictionary to [method safe_dict].
func get_dict() -> Dictionary:
if !is_open:
logger.errorf("storage", "Failed to get dictionary: No storage file is open")
@ -124,6 +146,8 @@ func get_dict() -> Dictionary:
logger.verbf("storage", "Returning storage dictionary")
return storage
# +++ raw manipulation +++
## Saves a arbitrary dictionary as a [param storage] [class Dictionary] with sanity checking ([code]sanity_check[/code] and [code]fail_on_sanity_check[/code]).
func save_dict(dict: Dictionary, sanity_check: bool = true, fail_on_sanity_check: bool = false, autosave: bool = true) -> bool:
if !is_open:
logger.errorf("storage", "Failed to save dictionary: No storage file is open")
@ -145,6 +169,8 @@ func save_dict(dict: Dictionary, sanity_check: bool = true, fail_on_sanity_check
if autosave: save_storage()
return true
# +++ etc +++
## Performs sanity checks on a [class Dictionary] to determine if it can be saved and loaded using this module.
func perform_sanity_check(storage_check: Dictionary) -> Array[String]:
logger.verbf("storage", "Performing a sanity check on some storage dictionary")
var errors: Array[String] = []