summary: eggine2d started as a hobby project and still is a hobby project, and as of right now it isn't really finished. eggine2d uses PIXIJS to display graphics and uses a custom networking solution in order to facilitate multiplayer play. the main feature of the eggine is its portability. not only can the eggine run as a game server through NodeJS, it can be ported to the browser, or any number of mobile phones due to its basis in HTML and Javascript. a transpiler has been developed in order to build the eggine. it is required for working networking features, and also has an obfuscation mode that obfuscates all method, property, and class names to make reverse engineering the eggine more of a challenge. i do not want potential hackers to have an easy time ruining whatever multiplayer game is made using the eggine.
PIXIJS is a very versatile graphics library that provides many rendering functions, primarily dealing with 2D sprites/objects. most importantly, the PIXIJS library allows users to access the "barebones" OpenGL state if needed by the user, which i have used to implement a lighting engine.
the eggine uses this library to render its graphics to a few different layers:
- the static layer, rendered ontop of everything and unmovable
- the dynamic layer, rendered below the static layer and moves along with a camera object
- the lighting layer, rendered ontop of the dynamic layer and moves along with the camera object. a shadow mask layer is used to determine whether or not a portion of the screen is under shadow, and light is not rendered in this region
using these layers, i can render whatever i want. if i am working on a game that requires more layers, then i am easily able to implement them and add them to the already existing layers.
additionally, the renderer has a custom chunking system that reduces the load on PIXIJS for rendering. basically, this functions as frustum culling. the world is broken into an infinite amount of chunks of a discrete size. objects are grouped into these chunks, and when a chunk is not seen on the camera the chunk is forcibily not rendered.
the networking has been inspired by how the Unreal Engine has implemented its networking features. websockets are used because i have no other alternative. on the server side, compression is used to reduce the amount of data sent in the websocket connection. the goal with networking is to clone an entire game state from server to client. in order to do this, we need to send a perfect copy of every object that makes up this game state to the client. every object that contributes to this game state is called a Remote Object, because it will inevitably be sent remotely over the network to the client. Remote Objects each have an unique ID that is used by the server and client to reference a cloned object. the unique ID allows the network to optimize a few things required for additional features. such features include:
- remote methods which can be executed by the client on the server or vice versa. remote methods are inherintly dangerous, since the client can send bogus information to a method, or even spoof the method that they want to execute. this is mitigated by having a whitelist of allowed remote methods and a parameter verification system that checks the types of parameters sent by the client. the verification system also can check the range of such parameters, throwing out parameters that are of the correct type but are still unexpected by the remote method. remote methods are integrated in such a way that allows the programmer to call any method that has been marked as a remote one, reducing code complexity. a Typescript Decorator will automatically determine to request the server or client to execute the remote method
- remote returns allow data to be automatically forwarded to the client or server after a remote method is executed. this feature is experimental right now, but eventually i want to be able to simply access a remote return as you would access a normal method's return. a remote return works by creating a promise that is resolved once data from the other end of the network has been received. the promise can simply be
await
ed to access the value of the remote return - remote fields allow a field of an object be automatically forwarded from the server to the client when the value is changed. this feature is not implemented yet, but was in previous versions of the eggine. i have not implemented yet because i never had a use for the feature, since my implementations always left more to be desired. however, now that i know how to use Typescript Decorators, i should be able to implement remote fields in a way that makes them not entirely worthless. remote fields will cut down on the amount of remote methods that are simply setters and getters, like
setHealth
andgetHealth
. instead, a fieldhealth
could be created on a class and be marked as remote, which will then be automatically forwarded to clients when modified by the server - remote objects are simplified down to their remote ID during transfer. if a remote method takes a remote object as a parameter, a "pointer" to that remote object is created using its remote ID and allows the server or client to find the intended remote object in their local network scope
remote objects are cloned by recursing through all of their properties. if a property contains a reference to another remote object, it is simplified down to a remote "pointer" during transfer, to reduce the amount of data sent. the remote object contianed by that property will be sent in full later regardless. the properties are sanitized to make sure they can be converted to JSON successfully. once the JSON is received on the other end of the network, the remote object is recreated from the JSON. a classname is contained in the JSON and is used to create a new instance of the specified class using Object.create
, and all properties contained in the JSON are copied over to the new class instance. additionally, remote object pointers are replaced with the references to the same remote objects that have been created locally using the remote object recreation process. at the end of the process, the cloned remote object is just that; a clone.