Eulora's Core in Glorious Detail (and Picture!)

May 17th, 2021 by Diana Coman

The initial work only expanded in scope in the intervening years to the point where it ended up covering pretty much everything, from end to end encryption, rsa-based authentication and a new protocol for client-server communication, to flexible file transfer torrents-style, a full graphics pipeline operating on demand, user interaction (both GUI and CLI) and various intricate mechanics that I won't even start detailing here if I am to keep this introductory paragraph from taking over the article whole. Nevertheless, as all this extensive ground has been covered in at least one fully working iteration and the swamps have been all but drained, the core structure is now reliably in place and so the time has come to document publicly at least the very Core of what will be the most widely available part of it, namely Eulora's Client.

Looking at the dates, I notice only now that it's almost exactly two years since I published the first version, higher level map of client's core. It's been two very productive years though, so that almost all of the User Interface part got trimmed to a more reasonable size, eviscerated and squeezed like the paste that it was into usefulness, while the Core part grew into providing full services on top of the barebones, raw elements that were originally listed.

The client's Core is still a library but it has a clear single entry point in the EuCore package that manages all the various parts and provides the means for a C/CPP main to start and stop the whole in a controlled manner, without being otherwise aware in the least of any of the Core's own internals. Basically EuCore serves as a control interface, ensuring that even a C/CPP main can take advantage of Ada's neat mechanisms1 for clean and tidy multi-threaded execution. On receiving the signal to start everything, this control interface initializes (aka asks them in turn to load whatever they need from the shared configuration file, already) and starts all the components that it manages. On receiving the signal to stop, EuCore passes it on to each of the components it manages and otherwise doesn't have any qualms about killing off (via abort) if and when required any tasks that can't or won't stop in a less abrupt manner.

The most important components managed by EuCore would be, from the lowest level up:

  1. The sender/receiver tasks for RSA and Serpent packages simply provide in-memory queues that can be made therefore larger than the IP stack provides otherwise, for both outgoing and incoming UDP messages so that losses are kept to a minimum and callers from the rest of the code don't need to wait. They are both based on the same generic Snd_Rcv package simply instantiated with the corresponding package size.
  2. The Comm_Link package handles all the practical details and inherent complexity of keeping in sync over a lossy communication channel. This includes keeping in sync the two buffers of Serpent keys as well as providing everything required for overall key management. While this sounds little, in practice it's quite the opposite of little, no matter what you measure there. Note that this package provides everything required and handles its own internal logic but does not attempt anything beyond its station - the higher logic for communication management simply can not be at this level and so you'll find it in HeartBeat, further down this list.
  3. The EuCache package maintains and provides access to the knowledge tree of the game's world aka that boundless although finite data hierarchy. The role of EuCache is that of a librarian essentially: it accepts all new data as it becomes available, it integrates it with what it already knew and it aims to provide on demand more than just the raw data since raw data is rarely directly useful as such. What is more useful instead and what EuCache aims to provide in addition to the raw data is at least one level of integration on top of it, basically trying to take any useful tiny steps that it can towards that ideal known as knowledge. How far it gets and therefore how useful it turns out to be depends more on you and what you figure out to ask from it in due time. For now and for starters, the possibility is there, the examples are also there, the rest remains up to you.
  4. The Torrents package handles the rough as well as the fine mechanics of file assembly, storage and access. This includes receiving and fulfilling requests for retrieving files (whether local or remote), handling all the different bits and pieces of chopped files as they arrive over the network, checking each of them and asking for any corrupted or missing parts, as well as assembling the full file, storing it in a convenient manner (currently neatly ordered by type) at the path specified by the user in the configuration file and pointing any caller to the exact path for any specific or default file2, as requested.
  5. The HeartBeat package coordinates everything in Core at regular intervals. This contains also the higher level logic for it all, since that requires almost always repeated and/or time-based decisions, from requiring an update of the world's data to repeating a request that is still pending or deciding on resetting the link entirely when no other measures manage to recover the synchronization with the other end.
  6. At the outermost edge of Core, the EuInterface package wraps up and exports with the C convention (so that any C/CPP code can use it directly), all the functions and procedures that modules built on top of this Core may use - from asking the server for assets or information to sending back to the server any of the user's actions and everything in between, even building up and accessing a full local history of all user's console commands.

In addition to the more prominent packages briefly described above, there are also quite a few utilities packages that provide a common language of types used across the whole Core lib as well as the lower level tools required and employed by those higher level packages. At the very base of EuCore, there is thus the Raw_Types package that all the others rely on, since it defines all the data types required by the communication protocol, as well as structures, subtypes and conversion utilities that rely on these types and are widely needed otherwise for various mechanics. On top of Raw_Types but focusing on structures and utilities supporting knowledge acquisition rather than the low level protocol mechanics, there is Eulora_Objects that provides for instance the records and hashmap types for storing any euloran object as well as the full known hierarchy of the world at any given time.

In between the above two data structures packages and the higher level packages listed previously, there is a middle layer of tool providers basically, from simply logging something or obtaining raw octets from the currently set source of entropy to Keccak hashing, RSA-OAEP encryption or message packing/unpacking and checking.

A full graph of the dependencies between all the Ada packages in the client's Core current version shows quite naturally the top (EuCore) and bottom (Raw_Types) two packages as well as the strictly and clearly hierarchical structure overall: while there are indeed many links between packages as they are rather intensively working together3, there are no cycles and moreover, no dependencies going the wrong way, meaning from bottom to top. All the links go clearly and as they should, always and forever from top to bottom (the red rectangle surrounding Keccak is there simply to mark that Keccak-Hashing is its child package):


  1. Core's code currently makes use mainly of protected objects and task types/variables. 

  2. Eulora's client will be quite independent in deciding what it uses as defaults for any and every thing. There are no externally imposed or defined defaults because that doesn't solve anything since one can then simply not have the officially declared defaults and so what is that supposed to solve really, you'll end up with defaults for defaults or what exactly? So the cut made there is deeper but cleaner: the client will simply pick as default the first (as in oldest by its own timestamp) file that it has for each type and that's that. Yes, this means that different clients can easily have different defaults and it also means that you can even pick your own defaults if you care - just change the timestamp for the file you want so that it's oldest and that's all. 

  3. Yes, they *could* be further separated but at *the cost* of additional significant verbosity that really doesn't make anything clearer nor easier to follow - there is nothing "easier to follow" in introducing a few layers of indirection in there just to provide separation that otherwise is not really meaningful nor needed in any way. 

Comments feed: RSS 2.0

3 Responses to “Eulora's Core in Glorious Detail (and Picture!)”

  1. Mircea Popescu says:

    > and asking for any corrupted or missing parts

    asking again, as it were.

    Such a pleasure reading these by now. Nicely done, that graph's worth ten thousand locs.

  2. Diana Coman says:

    Even asking as many times as needed really, again and again and again...

    Glad to hear it and thanks!

  3. [...] prize for having a clean environment (not merely code, nor even limited to code) is refinement of precisely the most intellectually [...]

Leave a Reply