-
Notifications
You must be signed in to change notification settings - Fork 6
Engine Write up
The game engine features a basic loop system in emulation of a server-client setup. The basic running structure is as follows:
boot() -> load() -> pre_tick() -> tick() -> post_tick() -> pre_tick() ...
boot()
Boot sets up all clients in the game, including finding their API and giving them the object or objects they control. Clients are loaded in using the importlib module, the very same that supports the import keyword. It has specific functionality that allows for loading in a file, and combined with the os module's listdir method, we no longer require the exact name of the client's file to use it. Given that, we are capable of importing an unknown number of clients of unknown name. This is also a fine place to instantiate logic controllers.
load()
Load gets the generated map / world / cycle / etc and loads it into memory. It also looks to make sure that it exists. Because what is generated can vary so greatly between types of game, the variable type and usage of it also varies.
pre_tick()
Pre_tick has good general usage in compartmentalizing logic that should happen before the player's turn. It could be used for establishing the current world as described in the game map, or modifying certain described items for player usage.
tick()
This is where the bulk of the logic takes place. Here is where the players will take their turn and logic controllers apply their effects onto the world and players. Players require some extra care to prevent security issues and players from having undue control over the system. The system does implement a bit of multithreading to control how long a player can take to process their turn and so all players can take their turns simultaneously, speeding the system up. It uses the more modern system of threading where a basic thread object is created and pointed to each respective turn method the players own and modify. For additional security, an object is used as a communication source, effectively creating a one-time only memory location that the engine knows it can find the players' actions at. This object is created and sent to the player once a turn. Each thread is then started and given a limited amount of time to run. If they do not reply by the end of this time, likely due to either infinite loops or simply taking too long, they are dropped from the game.
The action object is the main source of communication between the engine and the players. It allows for contained logic on how the player can set up their turn actions or responses without accessing any critical components. It helps keep them away from any moving parts, basically. However, there are benefits, as we can set up numerous ways to make it easier for players to take their turn. Because the variables are hidden, the only way to interact is through methods, further securing the variables from malicious interactions.
The logic controllers are ways to compartmentalize logic into specific objects, keeping the main engine clean and allowing easy extension of functionality. Each controller has a simple enough interface where they receive the world or input and perform changes on it based on its specific functionality.
post_tick()
Post_tick is also a fairly basic function. At the end of each turn, the results need to be written to a file so it can be used by the visualizer. It's at this method that each individual file is created and the world is serialized (converted to a JSON compatible format). Each specific turn log can then be deserialized (converted from JSON back to its original format) by the visualizer for its own purpose.
Byte-le 2020