Ossa Sepia

August 9, 2025

My Public Euloran Key

Filed under: Coding,Eulora — Diana Coman @ 7:43 pm

It started, as usual, with players’ activity, this time automated bots and desire to gain access to such, payment concerns and the available avenues to reward useful effort. The conversation got longer than usual and spanning a couple of days, too, but perhaps the snippet below is the best introduction here, since it is quite part of what triggered this publishing of my key now rather than at a later time – if he’s going to play with Vamp on the keys side, might as well give him something specific to play with/against, right?

keyschat_1_640.jpg

While the client for Eulora 2 handles fully automatically the whole business of RSA keys and thus uniquely recognized identities, while the environment provides support for a Web of Trust (WoT) as well, the latest activity by players pushed suddenly quite strongly forward the private interactions and even rsa-backed contracts that people might want to have with one another. And since I am best placed to know exactly what is available, how it works and how it might serve this purpose, here’s my public euloran key made specifically for player to player interaction 1:

my player’s public key in Eulora 2

Simply download the above file, run your copy of vamp to unhex it and then use the result with Vamp as you would any other public key. If you publish or send over in Eulora 2 your key as well, we can try a full back and forth communication out.

Should add here that this is simply a public RSA key like any other – only plainly rather than opaquely stored and passed around. So to the extent that you can extract the actual RSA key out of whatever other software you happen to use 2, you can then in principle use that just as well with vamp 3, it doesn’t care.

  1. This may or may not be the same public key that I use for my euloran identity. Note that within Eulora 2 itself, the server is the central authority with which everyone has to authenticate and maintain the unbroken authenticated communication in order to appear in game as the respective character that they are handling. So within the game, if any X says something, it is indeed rsa-authenticated X saying that something – at least as long and to the extent that you trust the server of the game, indeed.[]
  2. Kleopatra most likely if you are on windows, gpg on linux but in either case, attempting to extract *just the key* is a good exercise to see for yourself just how little access to the actual controls the user is supposed to ever have. You know, one of those basic, repeatedly and respectfully cited everywhere Kerckhoffs’s principles, yes? []
  3. For completeness, the euloran RSA keys limit the length of the public exponent e, hence that trail of zeroes in the file above.[]

July 2, 2025

Euloran Kronikle II

Filed under: Coding,Eulora — Diana Coman @ 3:18 pm

~ This continues the Euloran Kronikle I ~

At Preposterously Prosperous Place

In the infinite land of Eulora 2, one can easily get lost if relying solely on the landscape for clues. As one player promptly found out when going to where the others should have been, only to be welcomed by an entirely empty space with no PCs nor NPCs in sight- is this part of the world the one I wanted, the one I needed or merely the one I happen to have stumbled in by accident, when going on auto-pilot and just looking away from the keyboard for a while? Luckily, the chat works from any and all places in and even out of the euloran world, so one doesn’t have to either wonder forever alone or indeed hope for no help at all. All it takes is to tell others of one’s trouble and there’s a good chance to receive some guidance towards finding one’s own way back:

e2k2_1_640.jpg
e2k2_2_640.jpg

As a wider benefit of both misadventure and telling others about it, the situation finally gave a good reason for the euloran world to give names to places – names that people can refer to, so that they can find their own way around and even tell others where they are, with some reasonable degree of confidence that they might even be talking indeed about the same place! So the world changed indeed and places got named, with the growing settlement where it all started finding itself in the very middle of Preposterously Prosperous Place (or PPP, for short). Some known places around seem to include at the moment the Limitless Ditch of Intoxication, the Cruel Mound of Pluck, the Secluded Course of Verve, the Goatish Hill of Destiny and the Saturnine Lane of Destiny:

e2k2_3_640.jpg

As for the explorer, all ended quite well for him too, as he got back to PPP and even found that, getting lost can really truly pay in all sorts of ways, when one is finally not lost anymore:

e2k2_4_640.jpg

Life and Death Get Vitals Moving

As PCs and NPCs truly got moving around the infinite lands, the very vitals of each of them also started moving – if in a more limited sense between the usual percentage values of 0 and 100 but decidedly up and down, as everyone noticed. Just as some vitals replenish with rest when at least enough food is had, others seem to drain with it. And then, one day, two of the oldest characters around were not quite that closely around anymore as the towncrier provided their epitaphs that gave perhaps some indication as to what could have done it for them only it didn’t, as well as what actually did it, indeed:

e2k2_5_640.jpg

As it turns out, one truly died of too effective ineffectualness, you know? This game might really be noticing something there…

An NPC’s Tease

While the best known NPCs are staying in town and providing services to everyone, there’s a growing number of less known NPCs that are busy with their own trials and tribulations in the vast lands of Eulora 2. One of those seems to have set up, whether intentionally or not, quite the tease for any player:

e2k2_7_640.jpg
e2k2_8_640.jpg

Despite being thus publicly exposed as the author of the previously unexplained phenomenon of apparently exploding amounts of bags appearing in place, he seems to be still at it. More recent reports suggest he can still be found by the trail he leaves behind:

e2k2_9_640.jpg

Merchant Dips Hands in Banking

The official merchant in Eulora 2, the one and only Rainer Tracksnarl recently dipped his hands in banking as well, to further improve the service he provides for his customers. Happily for eulorans everywhere, the official merchant’s hands are quite clean indeed, so that this dipping of his comes at no extra cost, while bringing the wanted benefit of being finally able to exchange between the many denominations of the euloran coin, the ECu. Players being human though, they quickly found, of course, that there is always more that they could want:

e2k2_6_640.jpg

One Towncrier Gets Busy: Daily Rankings and Other Notices

It all started – again! – with active players talking of what they did, where they are, what they found and what they think of it. As it happened before with the naming of places, it is the players’ own activity that pushes for some concrete improvement or addition – and in this case, it was the leadership boards that came out of this:

e2k2_11_640.jpg
e2k2_12_640.jpg

Less than a week later, everything was done and in place, so that the towncrier got quite busy indeed, with a brand new Notices and Announcement board that he is in charge of. So the towncrier is happily busy now, announcing all rankings as they get calculated and even pointing out the juiciest news in the more general, public chatroom:

e2k2_13_640.jpg

Better to CLI than to Click: the Stacking Experience

While NPCs benefit from Rainer Tracksnarl’s automated methods for tidying their inventory as they go, human players have had to do it manually until now. As activity increases though, the number of items grows as well and one might find (to their surprise or not) that clicking to move items about may be “very intuitive” but it quickly starts being rather too annoying, as well:

e2k2_14_640.jpg

As before, increased activity of the industrious and helpful kind also pushes for increased support from Eulora 2, so players got an improved bot command line so that the full stacking can take as little as one short line in the Console regardless of how many items there are that need moving. Alternatively, it can still take, of course, twice as many clicks as there are items to move, if one sticks to point-and-click rather than learning to use the command line. I’d say it’s far better for you to CLI than to Click but you are certainly free to take as long as you wish to find this out, there’s no rush at all:

e2k2_15_640.jpg

What Comes Next?

This one really and truly depends on players’ activity. As for the Kronikle, this part is easiest of them all – what comes next after this II is going to be III! When it will come out and whether it will still be manually compiled by me like these first two or not and what exactly it will contain and reveal from the euloran world is yet an open matter.

March 25, 2025

Euloran Shorthand for WoT Ratings

Filed under: Coding,Eulora — Diana Coman @ 5:07 pm

WoT 1 ratings are supposedly a shorthand themselves – just plain, integer numbers between -10 and +10, with every value meant to stand for something. For what, exactly? Well, given how few they are and how diverse the situations can be, the only possible answer is that it… depends and mostly on the rater, really. More to the point, these rating values can be used and assigned in any way one wants but are generally assumed to reflect to some extent at least, the degree to which one trusts one’s own knowledge and understanding of specific others, as they are met and interacted with.

But people being people, plain numbers as shorthand never really work as well as words where any meaning is intended. Hence, in practice, any consistent and systematic approach to actually using WoT ratings ends up quite quickly with at least one, possibly several word-based shorthand schemas for the ratings themselves. And while no such schema can ever be somehow “the one” or definitive or whatever else of this sort, there is perhaps something to be gained at least from making public a first such schema – as an example or maybe even just as a first attempt perhaps, if nothing else.

As to how this first schema even came to be – in Eulora 2, WoT ratings are used not just by players as they see fit but by the NPCs and generally by the game itself. Hence, there was the need for at least one specific schema that the code can rely on to give and interpret at the very least its own ratings – though possibly as well those of others in contexts where it is meant to interpret them. And it turned out in fact quite fun to attempt a consistent approach in finding meaningful shorthand names for all those rating values! So here is the current result, with negative values simply adding a “mis” prefix in front of the same word to mark the mis-alignment between rater and ratee:

  • 0 remembered (no negative here, it’s the one rating stating that one has interacted with this person and they want to keep this marked but they prefer not to give either a negative or a positive rating – hence, the rated person is simply remembered)
  • 1 contacted, -1 miscontacted
  • 2 acquainted, -2 misaquainted
  • 3 perceived, -3 misperceived
  • 4 encountered, -4 misencountered
  • 5 believed, -5 misbelieved
  • 6 known, -6 misknown
  • 7 trusted, -7 mistrusted
  • 8 respected, -8 misrespected
  • 9 admired, -9 misadmired
  • 10 fated (the main meaning of “fated” that I am using here is that of “determined” as in unchangeable in fact because this is pretty much what I see a 10/-10 rating validly standing for – the ratee is clearly and fully determined now as in the future, whether this is for instance because they are a known deterministic code bot or perhaps simply dead and thus not further able to either act or change at all, in any way), -10 misfated 2.

This is all, the first known public shorthand schema for WoT ratings, feel free to agree, disagree or even just argue with it, whether quietly on your own or more wordy in company, in the comments section below.

  1. Web of Trust, see what it is for, how it works and how to use it.[]
  2. According to the Oxford dictionary, the word “misfated” is “obsolete” and “last recorded around 1600s.” So I suppose I’m reviving words here, even if I didn’t set out to do this on purpose. I’m not even quite sure how or why would a word obtained via a perfectly plain and common prefix ever be obsolete but in any case, here it is then “last recorded” again, quite more recently than the 1600s.[]

January 21, 2025

Euloran Kronikle I

Filed under: Coding,Eulora — Diana Coman @ 6:25 pm

A ton of tubers a day keeps hunger at bay – or at least at a small cove, perhaps

Adventurous explorers in Eulora 2 reportedly found that there is at least some benefit to eating the tubers one digs. Even if not all around nutritious as it might be desired, they are at least not poisonous either and can even give a much needed (if at times rather small towards negligible) boost to one’s hitpoints (hp) and bloodpoints (bp). Imagine then if one could one day perhaps cook them into something even more useful than this…

kronik1_7_640.jpg

Crafting supplies are in but who has the money for it?

The long-awaited crafting supplies are indeed fully and freely 1available at the one and only official merchant, Rainer Tracksnarl. He duly advertised them in chat, players duly checked them out and barely managed to buy anything at all from all the piles:

kronik1_1_640.jpg

kronik1_2_640.jpg

One-legged craft-table reportedly crafts just fine

Active eulorans have recently spotted a one-legged craft-table in the wild, apparently crafting nevertheless just fine. As precarious as the whole thing looked at first sight, its contents proceeded to transform as expected and the output came in quite good looking too, so all is fine and in good euloran tradition – after all, if the table had two (or more!) legs to start with, what leg would there be left (or even right) for further improvement?

kronik1_4_640.jpg

kronik1_10_640.jpg

kronik1_3_640.jpg

How many clumps of grass does it take to make one coarse, frangible thread?

This is the question on eulorans’ lips, as crafting gets a slow but quite determined start. Opinions on the matter are diverse but also changing quite often, it would seem. From a reported starting 1 to anything between 4 and 5 currently, there seems to be no limit to it. And it all seems quite similar to an earlier public discussion on the little bits of nothing required to build a single tiny claim:

kronik1_5_640.jpg

kronik1_6_640.jpg

Merchant gets a grip on his runaway inventory

The prominent merchant of all things euloran, Rainer Tracksnarl, recently got a large shipment of crafting items of all sorts, neatly packed and carefully stacked by type and quality. The shipment is holding well but the stacking reportedly went quickly wild with all the players’ attention going through it, so that the merchant found himself with thousands of items scattered around in his inventory. While nothing was lost, lots was thus misplaced and even more was just taking up space, pushing desired items so far away that some said they could not even find them at all anymore.

Confronted with this unexpected issue of his runaway inventory, Tracksnarl finally took matters in his own methods and invested in a reliable re-stacking after each attempted exchange. Results so far look tidy, compact and utterly worth it, especially as players get to use the neat compact packing for their own inventories, too:

kronik1_11_640.jpg

NPCs 2 wonder out loud

As NPCs are increasingly active providing their services and answering questions in chat, it emerged that they may on occasion engage in conversations of their own. On one such occasion, they seem to be quite curious as to what humans might even be:

kronik1_8_640.jpg

kronik1_9_640.jpg

  1. Possibly “Free as in free speech, not free beer” or more plainly and aptly put: publicly traded, not gratuitously given.[]
  2. Non-playing characters. Because they work in the game, they dont’ just play it like PCs do, obviously.[]

June 14, 2024

One Infinity Is Lonely, Two Are Not Enough, so There’s Plenty in Eulora2

Filed under: Coding,Computer Graphics,Eulora — Diana Coman @ 7:07 pm

Quoth the log of last week to the day, the 7th of June:

Diana Coman: Meanwhile in e2 world expansions, it turns out that even infinite is nevertheless not enough for me since the expedient idea of setting the Underworld literally under the landscape may be expedient but it just kicks the can down the road and well, it’s my can so whatevers, I picked it up and upgraded the whole thing so that at least I solve more with it and have therefore now everything set to make in principle even the claims as separate dungeons for instance.
Vivian Sporepress: I gather now there can be an infinity of underworlds hiding in tiny spaces within the overall infinite world.
Diana Coman: Quite so. I guess the way to put it would be that this visible world is infinitely large but it’s just ONE world and at the very least death certainly takes one out of it, so there we go, adding the unseen dimensions.
Diana Coman: Anyways, next in line is therefore to decide on some different looks at least for the Underworld and make some crossing for starters so it can get put to use.

So it took two weeks and all sorts of work to design a visibly distinct graphics style, turn a crucial part of the code inside-out and add the unseen dimensions, add some teleporting effects as well since I was at it, play around and come up with the items to be endowed with such marvelous properties as world-crossing, test it all back and forth, consolidate everything, have the fun and take the screenshots, drink the wine, do the chores and take a break, too. This last part was just about the hardest to fit in, given that all the euloran environment is, finally, so raring to go, so willing to grow, so easy to add to that yes, it got to that point where there’s way more fun than work in working on it.

There’s rather less time for documenting the fun, as it tends to happen, but here’s at least a few screenshots to tell a bit of the tale – how my toon crossed over the euloran six – I mean sicksstyx- and found herself in another world, suitably darker in style, at the very least. I started in the good old world of Nowhere Land, studying quite intently the one single Obolus there was around – its looks were silly, its inscription quite a description and then its use should all be quite safe, too, as surely no obolus has ever been in another live person’s mouth before, has it? So in it went and its results were quite as promised, too:

underworld_1_640.jpg

underworld_2_640.jpg

underworld_4_640.jpg

Looking around and moving about a bit brought changes to the skyline and otherwise mostly more red and grays and a bit of blue, too, by the looks of it:
underworld_5_640.jpg
underworld_6_640.jpg

After a while though, one does wonder, perhaps, as to the other one side of this one side trip – how might it be to go back to a more colourful world, where others are, where merchants live and things can be found. Perhaps that broken lyre is perfectly broken indeed and might just serve as an anti-obolus, who knows. More importantly though, who is going to try it, to find out?
underworld_7_640.jpg

August 6, 2023

The Syntax of World Knowledge in Eulora2

Filed under: Coding,Eulora — Diana Coman @ 2:05 pm

~This is a work in progress, to be discussed and expanded as the game progresses further. This article brings together and builds on the work already done and the preliminary concepts that were already described in previous articles, including a first draft of a data hierarchy, its subsequent refinement and an initial brief discussion of how it works in practice.~

To support players in their quest to explore, make their name and write their own history in the infinite and infinitely expanding world of Eulora2, the game’s client acquires and maintains at all times a dynamic view of the game’s world, as it becomes accessible and known to each player and as it responds to their actions or as it changes otherwise, with time or due to other players’ actions and to the game’s own internal rules.

The above means specifically that the game’s client successfully retrieves and makes use of any new data in its relevant context on the fly, as it becomes available and without knowing it upfront – because it simply can *not* be known upfront, since it’s the players’ actions that decide the very evolution of the game’s world! Playing Eulora2 truly means exploration and discovery in full, not just a pretense kept alive by some sort of agreement between server and client to merely not show or not use some data until the server says it’s “available” and the client is “updated” to have it and be able to handle it. In Eulora2, the player doesn’t need to avoid for whatever reason to even look at the files on their own disk. Quite on the contrary and unlike in any other game to date, in Eulora2 the player is warmly invited to look at the files on their own disk, to make full use of all the data and knowledge that they can access at any time, to write or use their own bots and anything and everything else that they can gather or even think of.

The game’s own client as I made it to date supports the above by design, as it is entirely and fully dedicated to serve the player with all it has and all it can actively and continuously acquire. These are not empty words but fundamental principles of mine and thus I made the client according to them, quite on purpose: it continuously checks, discovers and acquires new data and new knowledge of the world as it becomes available and it makes full use of it and of any new assets on the fly and as unobtrusively and quickly as it can.

How is such continuous discovery of knowledge and on the fly use of new assets even possible? In short, it’s made possible by knowing the very syntax of the game’s world representation – thus, instead of trying to enumerate upfront the world entire and getting stuck as soon as a new element is added, my client for Eulora2 simply starts off knowing how to discover and make sense of new elements as they appear.

My client starts without any of the game data but with the full ability to acquire and make sense of any and all such data, as soon as it has a working connection to the server. The way it does this is by knowing the rules of how the Euloran world describes itself, the very syntax of world knowledge in Eulora2. Unlike the usual game clients that start by having upfront a ton of data and graphics assets, my client starts without any of those but with a full working knowledge of the solid structure that contains and gives meaning to such data within the game. In other words, my client starts knowing how to obtain and make sense of data and then it proceeds to do exactly that, for as long as it runs and handling without any trouble all sorts of new situations as they appear, supporting thus the expansion of the player’s own increasing knowledge of the game’s world as it evolves with active play.

Knowledge of the game’s world as seen by any given player in Eulora2 is at all times a tree – a finite tree for sure but one that can have any number of branches and any number of levels, changing continuously as well, in response to the player’s actions and to the evolution of the game’s world as the time passes. To support the player, my client repeatedly explores this tree, pruning dead branches, growing new ones as relevant, updating and further interrogating parts of current interest and showing to the user the corresponding world view and its changes as they are obtained from the server in response to specific requests 1. Such showing of the world view is done through the graphical user interface (GUI) by default and through command line on demand. Playing the game relying entirely on the GUI alone is certainly possible but like all and any exclusive reliance on GUIs alone, this is a very limiting proposition really. Up to each player though how they play the game at any given time, for sure, but if you choose to limit yourself in some way, don’t expect that everyone else will necessarily limit themselves the same way, that’s all.

The above approach works to make sense of new content as it appears for a very simple reason: while the exact tree of knowledge for each player will be different and will keep changing throughout the game, its structure will always be syntactically correct, obeying thus at all times the known rules of describing the Euloran world. And since the game is by design and with intent fully open and encouraging players and developers to make or tweak their own clients as it serves them best, this syntax is public and will remain so at all stages. Moreover, any new additions to it will expand its capabilities as needed but without breaking at any point the compatibility with what came before – meaning that there is no requirement to “update” anything at any time. There is the option to do so or not, as and if it suits, nothing more. Like all Euloran evolution, even the world’s own language and corresponding syntax may certainly expand but in an organic manner, opening up new options on top of and building on existing ones, not contradicting nor “obsoleting” anything. In Eulora2 and as a fundamental principle, experience is an asset that keeps on giving – if one builds it up and uses it for all its worth, of course.

As for this knowledge tree itself, let’s illustrate it with an actual small example first and see how it describes the Euloran world or, more accurately, how it describes what one player happens to know about the game world at one given time:

(null, null)
- (Self, 23523903281)
-- (PlayerID, 123503920)
--- [10950238522]
-- (Salt, 8908319)
--- [2a891d0f] 2
- (Sector, 513250013)
-- (NameVal, 120945693)
--- [Waypoint]
-- (Ground, 935410)
--- (CSModel, 10853092)
---- [ea3420d8982]
-- (Sky, 1930582)
--- (CSModel, 8273509)
---- [8ff24351ab92c]
-- (PC, 10950238522)
--- (NameVal, 82082501)
---- [Edward Wealthgore]
--- (Cal3DModel, 98357102)
---- [183cca82bf82e]
--- (Image, 125935601)
---- [1f98eb246caa28]
--- (PosRot, 2795927491)
---- [101, 252, 114, 124, 42, 90]
--- (Item, 9873592011)
---- (NameVal, 873798211)
----- [Coarse Cordage]
---- (CSModel, 17349272)
----- [ff32ea1d89cb0]
---- (Image, 72985711)
----- [91a0b3f28ea8d]

Reading the above would yield the following description of the world: starting from the root of the tree (that null,null first line), there is first of a all a Self (with ID 23523903281) and a Sector (ID 513250013). Further asking the server as to what exactly that Self with id 23523903281 contains yields as children nodes a PlayerID (ID 123503920) and a Salt (8908319). Both PlayerID and Salt are leaf types in our tree and this means that upon asking the server to expand further on these two IDs (123503920 and 8908319), the response will provide directly values rather than further tuples (type,ID): the actual ID of the player’s own character is thus revealed to be currently 10950238522, while the value of the Salt turns out to be 2a891d0f, standing for a file with this exact keccak sum 3.

Going back up in the tree and asking for further information about the Sector (ID 513250013) reveals that it contains a NameVal (ID 120945693), a Ground (ID 935410), a Sky (ID 1930582) and one PC with ID 10950238522. At which point, the client will have found therefore the player’s own character in game, since this ID is exactly the one indicated by that earlier PlayerID node. Given that there is currently no other PC in this sector, it seems that for the time being, this player is either alone in the world or at least not yet seeing anyone else – possibly others are somewhere in the world but not yet within interacting/seeing range.

Drilling deeper into this PC node will reveal that the player’s character goes by the name of Edward Wealthgore, is located at some specific point as given by the value of that PosRot node, carries a Coarse Cordage item and uses as 3D representation as obtained from the graphics assets packed in the file with keccak hash 183cca82bf82e (the value of the Cal3DModel node) and as 2D representation what can be obtained using the file with keccak hash 1f98eb246caa28.

Repeating the same sort of exploration as above for the remaining nodes, in turn, there will be detailed information about each, as relevant, including any contained nodes as well as graphical representations and additional characteristics. I’ll leave the detailed description of the remaining nodes in the above example as an exercise for any reader wishing to test their understanding with a practical application.

While the above may seem tedious when written down in full detail, note that it’s done at all stages internally, automatically and extremely quickly by the client code – it’s exactly the perfect task for computers anyway, using a well and clearly defined structure to hold and process data repeatedly. As the player moves about, they are likely to encounter others and perhaps items too and thus more PC and Item nodes, each with their own IDs, will appear as children of that same Sector node above. If the user drops the Coarse Cordage that they are currently carrying, the corresponding Item node will simply move from its current place as child of the player’s own PC node to become a direct child of the Sector node and perhaps other characteristics of the PC node (not shown in this minimal example) will change to reflect the lighter load carried. Similarly, if the user picks up an item, that item’s node will move to become a direct child of the player’s own PC node and other values may change as well. When the user starts some work, a corresponding node will appear, holding the relevant information and allowing thus the client to provide feedback to the user as to the effect of their action in the world etc.

As to obtaining new data and assets as they become available, the mechanism is the same throughout: on-demand discovery followed by explicit request. For instance, when the client encounters in the world tree a filename that it doesn’t currently have, it will simply request it from the server according to the lower level communication protocol that fully supports such requests and then proceed to use the file as soon as it’s fully downloaded and checked to match the expected hash. Similarly, when it encounters a new node where previously there was none, it will request further information drilling down repeatedly until it gets to the leaves and it knows thus that there is no further information to obtain.

Following exactly that earlier mentioned principle of the client aiming to fully serve the user and empower them to make use of any and all information whenever available, the player can directly interrogate and explore themselves the current tree at any time, simply via an ls command in the game’s own console, quite similarly to how one explores the tree of files on disk from the command line on any computer. Note that this is made available without being either mandatory to use nor in the way of play otherwise – the whole point is to enable and support users at any and all stages, not to force on them anything and especially not to overwhelm them with more information than they request or are able to handle at any given time. Unless asked otherwise, the client will happily do all the above in the background and simply present the results in graphical form as accurately as possible but whenever the user wants to dig deeper and take advantage of the additional flexibility and power that a command line always offers over a graphical user interface, the option is and remains entirely available.

It’s worth noting that the graphical user interface (GUI) is always and at all times a simplified and thus reduced set of options for interaction, never the reference as to what can be done or accomplished. This is simply because it can never be any other way really – the way computers work is always through a set of rules that are quite unaware and unimpended by whatever limitations are introduced by the GUI that has to pick some set of shortcuts essentially out of the full set of real possibilities. Hence, while the game can certainly be fully played through the graphical interface alone and the GUI – or at least the one I make and maintain – will at all times aim to support the user as much as possible, it’s really unlikely that any implementation of a GUI will be ever able to provide in full the same flexibility as the command line offers. For this reason, my client at least has and will always have the command line integrated, alongside the GUI, as the more flexible and ultimately more powerful way to explore and act. Moreover, since the command line is always the reference as to what can be tried and attempted too, be aware that if you limit yourself to the GUI alone, you are more likely than not to miss out.

As for the syntax of world knowledge in Eulora2, the current set of node types and their possible direct children is the following:

ID Node Type Possible children types (if not leaf) Value type (if leaf)
null null 4 Self, Sector, GUI, Chat N/A
0 Sector NameVal, DateTime, CSModel, Ground, Water, Sky, LightSource, PC, NPC, Item N/A
1 Self PlayerID, Salt, WOTFile, DateTime 5 N/A
2 DateTime N/A 3*uint8 for hour, day, month + 2*uint64 for year and era
3 PC NameVal, PosRot, Cal3DModel, Image, Particles, Work, Exchange, Item, Vitals, DateTime 6, Unit_Size, Unit_Capacity, Inventory_List N/A
4 NPC NameVal, PosRot, Cal3DModel, Image, Particles, Dialog, Item, Vitals, DateTime 6, Unit_Size, Unit_Capacity, Inventory_List N/A
5 Item NameVal, PosRot, Image, CSModel, Stack, Item 7, Description, TextLine, Flag 8, Category, RefID 9, Unit_Size, Unit_Capacity, Inventory_List N/A
6 Particles NameVal, Image, Light N/A
7 PlayerID N/A uint32 (4 bytes)
8 NameVal N/A Text
9 PosRot N/A 3*uint16 for position, 3*uint8 for rotation
10 Stack N/A 2*uint64 for quality and count, uint32 for slot number, uint16 for version
11 Cal3DModel N/A text for filename, 16 octets for keccak hash of file
12 CSModel N/A text for filename, 16 octets for keccak hash of file
13 Image N/A text for filename, 16 octets for keccak hash of file
14 Ground NameVal, CSModel, PosRot N/A
15 Water NameVal, CSModel, PosRot N/A
16 Sky NameVal, CSModel, PosRot N/A
17 LightSource NameVal, Light, CSModel, PosRot N/A
18 Light N/A 3*uint8 for r,g,b colours, uint16 for radius of effect
19 Salt N/A text for filename, 16 octets for keccak hash of file
20 Dialog TextLine, Beg, Bribe, Threaten 10 N/A
21 Beg N/A text
22 Bribe N/A text
23 Threaten N/A text
24 TextLine N/A text
25 Work NameVal, Percent, NPCID N/A
26 NPCID N/A uint32
27 Percent N/A uint8
28 GUI Skin, Branding N/A
29 Branding N/A text for filename, 16 octets for keccak hash of file
30 Skin N/A text for filename, 16 octets for keccak hash of file
31 Exchange PartnerID, Give, Receive N/A
31 Give Flag 11, Item, FixedList N/A
32 Receive Flag 11, Item, FixedList N/A
34 PartnerID N/A uint32
35 Flag N/A uint8
36 Chat NameVal, Lowest, Highest, Description N/A
37 Lowest N/A uint64
38 Highest N/A uint64
39 Description N/A Text
40 WOTFile N/A text for filename, 16 octets for keccak hash of file
41 Vitals N/A 12*uint8 for percentage of each vital
42 Skill NameVal, Description, Level N/A
43 Level N/A 2*uint8 for experience and knowledge, respectively, followed by 1*uint64 for rank
44 Category N/A Text
45 RefID N/A uint32
46 FixedList N/A count (uint8), count*(uint32, uint64) for count pairs of id,quantity
47 Training PartnerID, Flag, FixedList N/A
48 Rating PartnerID, Flag, Description, DateTime N/A
48 Unit_Size 12 N/A weight (uint32), bulk (uint32)
48 Unit_Capacity 13 N/A weight (uint32), bulk (uint32)
49 Inventory_List 14 N/A version (uint16) 15, n (uint8), n * (count uint64, quality uint64, SHC 16, 6 octets)

In addition to the above, some constants have a fixed assigned meaning in their respective contexts:

Context Constant Values and Meaning
requesting to attempt a specific activity ID of activity 0 for Explore (and default);
1 for Use

The above syntax has been in use successfully for a couple of years already so it’s not going to change fundamentally. New node types are likely to be added as the implementation proceeds further but they will be added to this list, as needed. Even at this time, I don’t quite see the case for removing some node types and even less so for altering them since that would indeed be the only way in which compatibility is broken. Nevertheless, until access to the client is widely open to everyone, consider the list above as a work in progress rather than fixed and otherwise as an invitation and opportunity to get involved perhaps, if you do your own implementation and have any questions related or otherwise relevant to it.

Any related discussion is welcome and can best happen in the comments below, as usual.

  1. Note that the server for Eulora2 serves essentially as an oracle for the game – it answers any and all queries but it never pushes information unasked. This is by design, fully intended as such and entirely unlikely to ever change. There’s a wide scope for client implementations to compete though on different approaches to best make use of the server oracle for their users and thus provide an in-game advantage that players will be willing to pay for.[]
  2. This as all numbers in here are given as example only and as such, rather shorter than the actual values.[]
  3. Given here in hex for easy distinction of file hashes vs other values so it’s easier to follow with an actual client at hand, since the client uses this hex string to name the file on disk. Otherwise the value of that keccak sum is sent from the server directly as a number as obtained from keccak, of course.[]
  4. This is by convention the root of the tree and there is always at all times one single root.[]
  5. Expiry date meaning the in-game time until when current sustenance lasts.[]
  6. birth date, if known[][]
  7. This is because some items can contain other items, of course.[]
  8. Currently used masks over this are 0x10 for Locked, 0x02 for Inaccessible[]
  9. referenced object, if any, e.g. keys reference the item they unlock[]
  10. This was an early test and it works fine but meanwhile my design moved away from pretending to have “dialog” with bots and towards an altogether more promising quest system. For now though this node is still possible to encounter, indeed, hence it’s shown in here.[]
  11. 0x10 for locked, 0x0C for approved[][]
  12. This is the approximate size of one unit of whatever the parent is, given in some consistently-used unit across the whole game at any given time.[]
  13. This is the approximate capacity of one unit of whatever the parent is, given in some consistently-used unit across the whole game at any given time.[]
  14. A faster but more restricted view of the items contained in something – the server decides at all times whether this is used or not.[]
  15. Increased by at least 1 and at most 255 at each change.[]
  16. Shorthand code, unique across basic items in Eulora 2, obtained as upper case letters as found in order in the item’s basic name excluding any additional prefixes and suffixes[]

July 10, 2023

Static Standalone Zip Lib for C or Ada Use

Filed under: Coding,Eulora — Diana Coman @ 5:23 pm

‘Why?’
Because I need and use it.

‘What?’
The full, C-only code for zip archive operations of all sorts, entirely standalone and free of autotools and the like, ready for my intended use directly from Ada code and easily extendable for any other specific use from either C or Ada. It produces and uses standard zip archives. It comes with all needed headers, bells and whistles, a minimal .gpr file for compilation with GNAT as a static library and some commented out examples, too. The heaviest part of it is, unsurprisingly, the code for zip operations that weighs in at 10130 lines in miniz.h. The rest of the code adds about 2400 lines more in total.

To expand a tiny bit on the above, I’m using this exact code as part of the fully automated graphics pipeline that generates on demand any and all graphics that Eulora2 needs, as it needs it. I haven’t bothered to “clean” the code to any specific extent beyond the very functional – it either serves or at least stays out of my way. As such, I think the code as it is currently makes, if anything, a good place to start for someone wanting to learn to read effectively code, perhaps – it’s reasonably big without being too big and otherwise written and packed at all points by people focused on their own needs for it, not on ticking something off some check lists or using up some time at work.

I’m publishing the code as a 1st edition of MiniZipLib, in VaMP format, of course, since it can be perhaps useful to others if/when they want to avoid depending on external code for any zip-related needs.

Note also that the client for Eulora2 relies currently on something else, essentially external zip code even if this external dependency happens indirectly, via CrystalSpace. This is because all graphics assets are stored and used as .zip files that group together the various parts and pieces that are required at times to make a full model or item. While my current main interest and use for this lib is mainly serverside, the client could certainly benefit as well from reducing its dependency on external code and integrating this. Currently I don’t have the time to do it though as the further development of the game itself takes precedence but I’m making the VaMP tree for it public via this 1st edition, so that others can pick it up if they need it.

As usual, feel free to ask relevant questions in the comments, below. If you don’t know and need to find out where the code is thus published, it’s likely though that you are still missing an earlier step, namely interacting with people so read perhaps a bit more around and see what pulls you in so that there is some better starting point for it all than mere code ever was or ever will be.

June 13, 2023

The Communication Protocol for Eulora2, Restated

Filed under: Coding,Eulora — Diana Coman @ 12:48 pm


As the game‘s development is progressing beyond the emergence of time, past the addition of vitals and into enabling player activities, all the design and implementation work done so far feeds as well into clarifying and refining the game-related parts of the communication protocol that were previously tentatively and incompletely stated 1. Specifically, it’s again that ‘Character Actions’ section 7 that comes into focus but this time the changes go beyond just simple additions and move things forward from where the previous effort towards specification stopped. Consequently, I have to bring together the different parts and restate the current take on the protocol in full, in here, where it can be further iterated and discussed as needed:


This is the current take on Eulora2’s communication protocol, last revised June 13th, 2023.

1. Overall Goals:

  • 1.1. All communications between clients and server to be encrypted.
  • 1.2. Clients to be able to receive from server any data they lack (including maps, skins, sound or video content etcetera), on demand.
  • 1.3. Clients to be able to choose and adjust both the level of security and their volume of communications with the server, as they will ultimately have to pay for the load that they generate.

2. Explicit Dependencies :

3. Data Structures :

    3.0. Basic types :

    • char / uint8 (1 byte) ;
    • uint16 (2 byte) ;
    • uint32 (4 byte) ;
    • uint64 (8 byte) ;
    • float 2 (4 byte) ;

    3.1. Special types:

    • hash (128 bits) ;
    • chunk [of file] (bitfield, 11760 bits) ;
    • serpent-packet (1472 bytes) ;
    • rsa-message 3 (1872 bits 4) ;
    • rsa-packet 5 (1470 bytes) ;
    • object (size of 104 bits 6: uint32 7 followed by 3 uint16s representing position 8 followed by 3 uint8s representing rotation 9 ) ;
    • legacy-text (size of n+n/256+1 bytes ; where the leading byte is the bytecount of the 2nd segment and the 2nd segment is the bytecount of the third segment) 10.
    • text (2 byte hearder containing the ~total~ byte length ; up to 1470 bytes of text ).

4. Serpent Packets 11 :

    4.1. Serpent Key Set:

    • uint8 (type ID, =100), followed by
    • uint8 (count of keys in this set, n), followed by
    • n*(4*int64 + uint32) (32 bytes each key followed by a 4 byte ID calculated through crc32 12 ), followed by
    • an uint8 flag (LSB bit set — keys to be used to talk to client ; MSB set — key to be used to talk to server ; client-set MSB is ignored), followed by
    • uint16 (message count 13), followed by
    • padding to Serpent-message length.

    4.2. Serpent Keys Lifecycle Management:

    • uint8 (type ID, =102), followed by
    • uint8 (count of server keys requested), followed by
    • uint8 (count of client keys requested), followed by
    • uint8 (id 14 of serpent key preferred for further inbound Serpent-messages), followed by
    • uint8 (count of burned keys in this message), followed by
    • n*int8 (id of burned key), followed by
    • uint16 (message count), followed by
    • padding to Serpent-message length.

    4.4.a. File Request, manifest

    • uint8 (type ID, =3), followed by
    • hash (corresponding to the sought file 15), followed by
    • uint8 (manifest packets sought count, 0=all), followed by
    • n* uint16 (manifest packet index sought), followed by
    • padding to Serpent-message length.

    4.4.b. File Transfer, manifest (always sent and only sent in response to ID 3)

    • uint8 (type ID, =4), followed by
    • uint16 (count of manifest packets for this file 16), followed by
    • uint16 (index of current packet in list above), followed by
    • uint8 (fragment count 17), followed by
    • n* uint64 (hash of the nth fragment of manifested file).
    • uint16 (keccak hash of foregoing), followed by
    • padding to Serpent-message length.

    4.4.c. File Request, chunks

    • uint8 (type ID, =5), followed by
    • hash (corresponding to the sought file), followed by
    • uint8 (file chunks sought count), followed by
    • n* uint64 (the hash of fragment sought), followed by
    • padding to Serpent-message length.

    4.4.d. File Transfer, non-last chunk (always sent and only sent in response to ID 5)

    • uint8 (type ID, =6), followed by
    • chunk.

    4.4.f. File Transfer, last chunk (sent at most once per ID 3)

    • uint8 (type ID, =7), followed by
    • uint16 (bytesize of useful part of the chunk following 18, followed by
    • chunk 19.

    4.5. Client Action 20 :

    • uint8 (type ID, =8), followed by
    • text (fully specified action, see section 7), followed by
    • uint16 (message count), followed by
    • padding to Serpent-message length.

    4.6. World Bulletin 21:

    • uint8 (type ID, =9), followed by
    • uint32 (id of top level item 22), followed by
    • uint8 (count of objects), followed by
    • object list 23, followed by
    • uint16 (message count), followed by
    • padding to Serpent-message length.

    4.7. Object Request:

    • uint8 (type ID, =10), followed by
    • uint8 (count of objects), followed by
    • n*int32 (id of object), followed by
    • uint16 (message count), followed by
    • padding to Serpent-message length.

    4.8. Object Info:

    • uint8 (type ID, =11), followed by
    • uint8 (count of objects), followed by
    • n times uint32 (id of object) and text (object properties, as per extant game structures, including art files needed and so on 24), followed by
    • uint16 (message count), followed by
    • padding to Serpent-message length.

    4.9 Line Request

    • uint8 (type ID, =12), followed by
    • uint8 (count of lines), followed by
    • n times uint32 (id of chatroom), uint64 (lowest line number requested), uint64 (highest line number requested), followed by
    • padding to Serpent-message length

    4.10 Line Info

    • uint8 (type ID, =13), followed by
    • text (line content), followed by
    • uint32 (chatroom ID), followed by
    • uint32 (speaker ID), followed by
    • uint64 (line number), followed by
    • uint64 (keccak hash of the line’s content and number), followed by
    • padding to Serpent-message length

    Updated 30 Oct 2024: ratings are now fully integrated and used in-game, being fully handled via the relevant Rating node in the data hierarchy. Consequently there is no need anymore to have dedicated message types for discovery and retrieval, so the “rating request” and “rating info” message types below are removed entirely. Rating someone remains done as an action (see the action types further down) and discovery happens as part of the usual game play.

    4.11 Rating Request

    • uint8 (type ID, =14), followed by
    • uint8 (count of requests), followed by
    • n times uint32 (source ID), uint32 (target ID), followed by
    • uint16 (message count), followed by
    • padding to Serpent-message length

    4.12 Rating Info

    • uint8 (type ID, =15), followed by
    • text(rating comment 25, followed by
    • uint32 (source ID), followed by
    • uint32 (target ID), followed by
    • uint8 (rating value 26 ), followed by
    • uint16 (message count), followed by
    • padding to Serpent-message length

5. RSA Packets 27 :

    5.1. RSA key set 28.

    • uint8 (equal to 251 to indicate packet contains a new RSA key), followed by
    • uint8 (protocol version), followed by
    • uint16 (subversion), followed by
    • uint32 (IP of server 29), followed by
    • uint32 (IP of client 30), followed by
    • uint64 (keccak hash of client binary), followed by
    • uint64 (e of RSA key), followed by
    • uint8*490 (N of RSA key), followed by
    • uint64 (preferred padding — the magic value of 0x13370000 requests random padding ; all other values will be used as such, bitwise, ie like an infinite-length OTP consisting of the value repeated), followed by
    • uint16 (message count), followed by
    • padding to RSA-message length, 1424 (5616-8-8-16-32-64-64-3920-64-16) bits exactly.

    5.2. Serpent key set 31:

    • uint8 (equal to 157 to indicate packet contains new Serpent keys), followed by
    • uint8 (count of keys 32 in this set, n; n<=19 33), followed by
    • n*(4*int64 + uint32) (32 bytes each key followed by a 4 byte ID calculated as crc32 on the key itself), followed by
    • an uint8 flag (LSB bit set — keys to be used to talk to client ; MSB set — key to be used to talk to server ; client-set MSB is ignored by server ; server will set LSB on keys requested by client for its own use thus supporting clients with no trustworthy random generators of their own), followed by
    • uint16 (message count), followed by
    • padding to RSA-message length.

    6. Protocol Mechanics :

      6.0. All communications between server and client will consist of messages. These messages may be encrypted either via eucrypt.RSA or eucrypt.Serpent. All RSA-encrypted messages will be exactly 1`470 bytes in length ; all Serpent messages will be exactly 1`472 bytes in length 34 . The server will handle Serpent messages in preference of RSA messages (which are processed on an as-available basis). Clients that send garbage will be punished ; the costs involved (encryption/decryption ; generating entropy ; lookups and whatnots) will be pushed onto the client, for which reason writing the clients lightly pays off.

      6.1 The handshake works as follows:

      • New client issues 5.1 packet keyed to the server’s public key, including its own RSA key.
      • The client’s IP is recorded, and will have to be explicitly changed by the client later if needed. Server replies with 5.1 packet keyed to the client’s announced key, including its private RSA key for use by that client ; and with 5.2 packet containing key material for client’s use in keying messages to the server. If the client fails to provide its own set of serpent keys, the server will further issue it a set of serpent keys ; thenceforth the server will send more serpent keys mirroring the client’s supply, and will similarily mirror key burning and select operations on its own set.
      • Should the client’s IP change, it will issue a 5.1 packet keyed to the server public key immediately followed by a 5.1 packet keyed to the server’s original private key. The server will then update the client’s IP accordingly (this also trashes the extant Serpent keyset and triggers 5.2).
      • The bulk of communication is intended to go through the Serpent system ; outside of identification and bootstrap handshakes RSA isn’t used. Should either party believe the Serpent keysets’ve been FUBAR’d, a 5.2 packet will reset that keyset.

      6.2. The server will issue type 4.6 packets in response to relevant type 4.5 packets received — these can either signify the acceptance or the rejection of the client action, and the client must adjust its internal state accordingly.

    7. Character Actions :

      7.0. Lock:

      • uint8 (type ID, =0), followed by
      • uint8 (count of objects), followed by
      • n* uint32 (object ids). Defaults to currently targeted item.

      7.1. Make 35:

      • uint8 (type ID, =1), followed by
      • text (title of quest), followed by
      • u64 (count of how many different times can this quest be completed – note that the reward will be divided accordingly), followed by
      • uint32 (arbiter id, defaults to nearest town crier), followed by
      • uint32 (object id of a sample of the required item), followed by
      • uint64 (wanted quality, defaults to 1), followed by
      • uint64 (wanted quantity, defaults to 1), followed by
      • uint8 (expiry day, defaults to 1), followed by
      • uint8 (expiry month, defaults to 1), followed by
      • uint64 (expiry year, defaults to next year), followed by
      • uint32 (object id of reward item stack, no default)

      7.2. Attempt 36:

      • uint8 (type ID, =2). This is a request to start the chosen activity (see next field), followed by
      • uint32 (object id, indicating the intended activity, defaults to explore 37), followed by
      • uint32 (object id, defaults to current target or location, as relevant), followed by
      • uint32 (object id, defaults to currently equipped method, followed by 38)
      • uint32 (object id, defaults to currently equipped tool)

      7.3. Exchange:

      • uint8 (type ID, =3), followed by
      • uint32 (object id, the other party), followed by
      • uint32 (object id, the trade itself 39), followed by
      • uint8 (count of objects), followed by
      • n* uint32 (object ids) and uint64 (object count), followed by
      • uint8 (flag, set to 0x10 to lock a trade and to 0x0c to approve a trade previously locked by both players).

      7.4. Attack:

      • uint8 (type ID, =4), followed by
      • uint32 (object id, the other party), followed by
      • uint32 (object id, the battle itself (server set, exactly in the way trade works). This not currently implemented, except player setting itself the bomb results in instadeath.

      7.6. Move:

      • uint8 (type ID, =6), followed by
      • uint32 (destination id, defaults to current target), followed by
      • uint32 (slot id), followed by
      • uint32 (object id, of the item being moved), followed by
      • uint32 (quantity moved).

      7.7. Train:

      • uint8 (type ID, =7), followed by
      • uint32 (object id, the other party), followed by
      • uint32 (object id, the train session itself (server set, exactly in the way trade and battle work). This not currently muchly implemented, except some NPCs train for money — but will get greatly expanded asap. Meanwhile, it actually got both expanded and implemented, see the relevant comment for more details.

      7.8. Relocate:

      • uint8 (type ID, =8), followed by
      • object type, containing new client position.

      7.9 Say 40:

      • uint8 (type ID, =9), followed by
      • text (line content), followed by
      • uint32 (room ID), followed by
      • uint64 (keccak hash of the line’s content)

      7.10 Rate:

      • uint8 (type ID, =10), followed by
      • text (rating comment), followed by
      • uint32 (target ID)
      • uint8 (rating value)

      Please leave your comments below.

      1. This type of iterative refinement and interaction between design and implementation is neither new nor surprising to me, really. The only difference is that I’m doing now out of necessity all sides of this. Experience helps *a lot*, of course, even or perhaps especially beyond and above what one ‘expects’ or what one ‘wants’ or what one ‘signed up for’ and so on and so forth. It might even help in the sense that it makes it possible at all, at that, even if it doesn’t make it easy by any definition of the word.[]
      2. Floating point item deliberately not specified[]
      3. Each such message is OAEP-padded and then encrypted with a (3920 bit) RSA key. Three such messages are strung together to form a RSA packet. Because of the significant overhead involved (both in terms of space and time), Serpent-encrypted comms are preferred whenever feasible. []
      4. See TMSR-RSA OAEP padding for the principle and this discussion for details.[]
      5. This is the total size of a packet containing RSA-encrypted material. The useful size (ie payload) of such a packet is merely 702 bytes.[]
      6. We really really want to keep this down. 13 bytes is the lowest I can conceive of, but I would so not mind halving it.[]
      7. Representing the identifying hash of the object in question.

        We’re using the narrower size to save on network traffic — all the expenditure of another 32 bits here would buy us is de-ambiguation for cases where the count of objects around makes 1 in 2 billion collisions relevant. It doesn’t seem likely a client could support such abundance of objects.

        Note that the hashes used here are client-specific, the server doesn’t leak its own internal representation of objects to the clients.[]

      8. Coordinates X, Y and Z in that order. Because the map goes from -512 to +512, the relationship between the given figure (GF) and map coordinates (MC) is GF / factor_coord – max_coord = MC, where max_coord is 512 and factor_coord is 65535/(2*max_coord).[]
      9. As a full rotation is 2 pi, the relationship between the given figure (GF) and object rotation (OR) is GF / 128 * pi = OR.[]
      10. This arrangement permits the representation of arbitrarily large textfields (2nd segment can represent up to 115`792`089`237`316`195`423`570`985`008`687`907`853`269`984`665`640`564`039`457`584`007`913`129`639`936 bytes, which is more than enough space for all the text ever produced — or likely to ever be produced — by humanity) at the modest cost of a fixed 3 byte header.

        Unfortunately, it has no longer any utility for Eulora, since we’ve moved to fixed packets. I’m preserving it here because I really like it in the abstract and it has no other place to go.[]

      11. These packets consist of 92 successive 128 bit chunks, Serpent-enciphered individually. To extract the payload one splits the message into 92 16-byte chunks, deciphers them then collates the output into a final result. To produce the packet one cuts a 11`776 bit payload into 92 128-bit chunks, Serpent-enciphers them, and collates the results into the outbound packet.[]
      12. Polynomial generator 0x04c11db7. Keys with null IDs are discarded and regenerated.[]
      13. Each client and the server will keep a count of messages they sent each other. This value must be incremented on each subsequent message sent by no less than 1 and no more than 255.[]
      14. Keys are maintained by both client and server in an ordered ring buffer 256 elements long. The server will not send more keys than the total count of 0(absent)-keys in the respective buffer, irrespective of request count. If the message contains an unknown ID or otherwise is unprocessable, the issuance of a 5.2 packet is adequate response.[]
      15. This is the keccak hash of the actual file contents. By convention this hash rendered as a 32 alphanumeric character string is also used as the filename for the file in question.[]
      16. This system allows up to 65`536 manifest packets, adding up to potentially 11`993`088 (65`536 * 183) fragments representing a file of up to about 140 Gb (141`038`726`624 = 11`993`088 * 11`760 + 11`744 bits exactly). This will have to be sufficient.[]
      17. From 1 to 146 inclusive.[]
      18. This also means the protocol does not allow the transfer of files of certain sizes (within 8 bits of a multiple of 11760), which is fine with me.[]
      19. The final fragment of the file will have to be padded to length as per this spec. []
      20. This is never issued by the server.[]
      21. This is never issued by the client.[]
      22. As discussed in comments, the world is a hierarchical structure of objects within objects.[]
      23. This portion will get more clarification later on! []
      24. The complete list of these is currently exposed by the extant client, but in any case we’ll publish a complete schematic. The server will set the “target” of the player on the last object in the list.[]
      25. Limited to at most 1445 characters, matching the maximum length of a chat line.[]
      26. Valid rating values are integers between -10 and 10. For a given rating value here GR, the corresponding actual rating value RV is therefore calculated as: Min(GR, 20) – 10.[]
      27. These packets consist of three 490 byte successive chunks RSA-encrypted individually. To extract the payload one splits the message into three 490 byte chunks, RSA-decrypts and de-OAEP-pads each one, the collates the results into a final result. To produce the packet one cuts a 5`616 bit payload into three 1`872 bit chunks, OAEP-pads and encrypts them, and collates the results into the outbound packet.[]
      28. This is the manner in which new clients register their RSA key with the server (thereby opening a new game account). Later replacement of a registered key IS NOT POSSIBLE. Keep your client’s RSA key safe.

        This is also the manner through which IP changes for an account are registered with the server. See the Protocol Mechanics heading for details.[]

      29. This is used by the server when signalling to the client to talk to a different server (which is a thing for scaling, because different sectors will be handled by different servers).[]
      30. If the client doesn’t know its own IP, it’s acceptable for this to be zero. []
      31. This permits either client or server to declare Serpent keys via RSA. It is not mandatory (as there exists a Serpent-encapsulated mechanism for the same end) but entirely legal. The server will always respond with at least one 5.2 packet after an accepted 5.1 packet creates a new player account, consisting of 40 Serpent keys to be used to talk to the server. Should the client respond with any other packet than 5.2 or 4.1, the server will send a 2nd 5.2 packet, containing 40 Serpent keys for the client’s use. []
      32. Keys obtained through a 5.2 packet are always indexed in the client’s buffer in the order they were found in that packet, starting with the first position.[]
      33. A RSA packet has 702 total bytes available, of which 5 are used otherwise and the remainder of 697 are available for packing serpent keys, which take 36 bytes each (crc32 id inclusive).[]
      34. Length being actually how they’re sorted on the server side.[]
      35. This is meant as a production order, essentially. I expect it will get further refined as its implementation gets nearer. It already got further refined and more specifically defined as a way to issue/create Quests or “wanted” posters fundamentally, see the relevant comment on quests.[]
      36. This is a generic initiation of player activity and as such literally an attempt, with the contents defining the exact activity. It replaces both Explore and Repair from earlier versions of this protocol but it further stands in for *any* game-defined activity even ones that may appear only at a later time. It’s as generic as it gets and quite on purpose, since this is the network layer, not the game layer. For more details on the game-defined activities, see the description of the data hierarchy.[]
      37. Known activities are obtained like everything else in game as part of the data hierachy representing the client’s view of the world at any given time. The relevant grammar for it will be made public as it serves as the in-game protocol specification just as this serves as the network-level client-server protocol specification.[]
      38. Known in Eulora 1 as ‘recipe’ and ‘equipped in mind’. At this level, the protocol for Eulora2 is really more generic than that, without any loss otherwise whatsoever.[]
      39. This is set by server through a type 6 message for both players involved, the trade is an object like any other that the OP has to request. The server will also expire trades, enforce them etc.[]
      40. Note that this is an action and as such just a part of a 4.5 message, Client Action, where the overall message structure is specified.[]

June 5, 2023

The New Action Type That Almost Was

Filed under: Coding,Eulora — Diana Coman @ 3:32 pm

The quickly progressing work on euloran vitals and physical characteristics is pushing already the design – and the fun, obviously! – into previously unmentioned and thus entirely uncharted areas. This time it all started easily enough: given that at least some vitals certainly drain with activity, what does unrest truly do to one’s wellbeing, what is rest going to even look like and what can one really do -or not do, rather- about it for best results?

As all good questions, the above has, of course, a common-enough answer that isn’t much good really but still has to be properly considered, first of all: unrest tends to do more to one’s worsebeing rather than to one’s wellbeing and rest is certainly needed so introduce a ‘rest’ action that the player can choose to do whenever they feel like and the game permits! Which sounds reasonable enough and doable enough, to the point where I even sketched out a new addition to that section 7 of ‘character actions’ in the communications protocol, since it’s really not all that difficult to add or use: have another ID, give some duration in hours now that time is ticking away at everyone’s lives and use otherwise the existing infrastructure without much trouble at all. It would certainly work too, so what’s the problem with adding it, right?

Well, the problem starts with that ‘adding’ really, since the whole point of having a flexible and generic communications protocol is exactly to have a set of messages and structures that can serve a wide range of uses while being preferably as short as possible 1. So any addition to the protocol itself comes with a non-negligible cost and thus with a strong requirement for considering first whether there is indeed enough of a fundamentally significant difference compared with already existing structures to justify a new one 2. Unsurprisingly perhaps, it turns out that where euloran rest is concerned, there isn’t that strong a case for a new action type as such – or at least not yet!

So then, if resting is both possible and needed in Eulora2 but it’s not to be done in the common style of being an action, how is it going to happen? The very simple answer is that it will happen by *effect*. Meaning, specifically, that different items will have a resting effect when consumed or otherwise used (and their use or consumption might take some time, too, possibly).

Further, the above approach to rest implies, of course, that one has to have such an item to even be able to rest at all, which might, at a first encounter, seem quite the alien and possibly harsh imposition (everyone can rest whenever, all they have to do is go to sleep!!), except it’s not really – just try it and see how well it goes when you try to rest without having at the very least some reasonably safe (quiet and cosy don’t even come into it) place, enough peace of mind and enough food to keep hunger at bay. Rest is indeed basic in the sense that it’s a basic requirement for survival not in the sense that it’s somehow effortless or a given at all times.

As a side effect of the most positive kind from the above, it follows of course that one would do better to keep an eye on their levels of tiredness – at least in Eulora2, since this is what the context is here. Because if they fail to rest when needed, things aren’t likely to go that well and for sure they aren’t going to go forever just because it’s a game or something. Past a certain point of tiredness, the body would naturally shut down and thus rest indeed of sorts, only it’s not all that clear if, when or how one still wakes up from such last-resort rest that is indeed guaranteed and a ‘natural right’ of everyone 3, certainly.

In summary, in Eulora2 you’ll live either responsibly or very, very briefly indeed! Practical learning, at its best.

  1. And seriously, I can still fully recall the spidery mess of a ‘protocol’ that PS originally had and what a pain it was to first work with it at all, then attempt to clean it and finally sideline it enough until one could entirely get rid of it. That experience certainly is more than enough to teach one to NOT rush into adding stuff just because it’s possible.[]
  2. For the record, such was indeed the case with the Chat additions – while they *could* be fit into the already existing structures, such fit was a very poor one because the types of use are quite different fundamentally and have thus very different requirements.[]
  3. Weeell, maybe just about everyone? I don’t promise that there won’t be any immortals/vampires/rest-impaired creatures in Eulora2![]

May 30, 2023

The Many Moons of Euloran Time

Filed under: Coding,Eulora — Diana Coman @ 4:31 pm

The latest development in Eulora2 is the emergence of time itself, quietly but definitely ticking away at the hours that became days, then months and years of the very first era that eulorans ever knew. Euloran time is already past the half mark of the first month and it follows its own rules, whether anyone is aware of them or not. So far, there are at least some signs that some people have indeed noticed something new:

back in, and noticing a new euloran clock.

e2_clock_1_640.jpg

It’s possibly best if one starts becoming aware of the rules of time or at least aims to figure them out sooner rather than later, because the time in Eulora2 is definite indeed: definite in the same way that Maths is definite but also, perhaps of more pressing interest for eulorans, in the same way that Death is definite. On the more reassuring side, so far at least, Death is yet to be met with in Eulora2 but that’s about as much knowledge as anyone has on the matter.

As to the durations and even structure of euloran days, months and years, these are still to be observed. What I will let slip is that Eulora2 has several moons that might be abstract or concrete but that certainly have very concrete effects on the world as a whole. To some extent, it might perhaps be not even false to say that the very ticking of time is just one of the more visible such effects – the rest yet to be noticed, observed and perhaps made some sense of. Not that there is any rush to do or even to notice any of it – at least there isn’t anything to rush one just yet. It’s a young world after all, still emerging, unhurriedly but unstoppably, too.

The whole first era of Eulora2 will cover in fact exactly the interval when the core structure of the world and of its interaction principles is brought in and settled in place, piece by piece. Those present and active in this first era will be essentially the actual ancestors and prime movers quite literally – after all, they are taking the risk on a nascent world and so they get also, by rights, a special status and the unique chance to leave their inprint on each part as it emerges. And there certainly are quite a few parts in the works, with next on the list the actual vitals and physical characteristics of player (PC) and non-player characters (NPCs) just the same.

Time, in this sense of Eulora2 emergence, is quite literally only the beginning!

April 27, 2023

More Direct than Direct_IO or Choosing Ada over GNAT

Filed under: Coding,Eulora — Diana Coman @ 1:31 pm

Back when I started programming in Ada, I trusted the GNAT implementation of the Ada standard, mostly because I had no choice 1. A few years and a lot of experience with said GNAT implementation later, I know for a fact that the I/O packages of GNAT such as Direct_IO and Sequential_IO are broken and merely obfuscating rather than helping in any significant way.

By the time I discovered in 2021 just how broken and uselessly complex these packages truly were, there were already several places where the game‘s client relied on them. Unpleasant discovery for sure but even so, given that there was plenty more pressing work to do and I had managed at least to restrict the use of such packages enough so that their bugs didn’t get a chance to manifest in my code at all, I left the existing code as it was and simply avoided using these packages in code written *from that point on*. After all, the client is anyway open source and thus easily changed by anyone who cares enough about it – if anything, this sort of known and well understood issue makes exactly for a low hanging fruit to pick, something that can even help newcomers to get more easily and more satisfyingly involved.

Recently though, as other parts get finally integrated and the client is getting ever closer to release, I reviewed this matter again, especially as VaMP came into the picture as well. As a result, it turns out that there still is a place where I can’t quite leave hanging this exact sort of fruit, namely when it comes to reading and writing cryptographic keys to the disk. First, this is quite clearly a sensitive operation and as such one where the fewer dependencies and the greater the clarity the better at all times. Second, this is integrated into the client indeed but it’s part of a base layer that is not really client-specific and as such I would really keep it as clear from any unwanted GNAT dependencies as I can 2. So I took the time to review again all the i/o code and then to sort it out in the most straightforward way possible.

The solution turns out to be both short and sweet – all the sweeter for being in fact so obviously well supported by Ada as a language and by all the rest of the code that I have implemented so far. All it took was one short new package that takes advantage of the very thin wrappers I wrote for the read/write C library functions (over which GNAT wraps layers upon layers of obfuscation more than usefulness) and uses otherwise direct memory mapping to convert between any given type to its raw representation and back.

Such direct memory mapping for composed types relies in turn on a very tight specification of how such type should be represented in memory but this was absolutely no trouble at all for me currently since… I had already taken the time when I defined the needed types to fully specify their representation as well! So there it was, my own work done well in the past quite directly and obviously paying its dividends in the present and compounding the dividends for the future, too.

As for the relevant code, here’s the new mmap_io package, helpfully formatted already by my own VaMP 3 for publishing on the blog:

eucore/src/base_layer/src/mmap_io.adb

6_1 
	-- DC, 2023
6_2 

6_3 
with Ada.Directories; use Ada.Directories;
6_4 
with Interfaces.C; use Interfaces.C;
6_5 
with System;
6_6 

6_7 
with Raw_Types; use Raw_Types;
6_8 
with Raw_IO; use Raw_IO;
6_9 

6_10 
package body MMap_IO is
6_11 
	sz: constant size_t := T'Size / System.Storage_Unit;
6_12 
	procedure Write(tval: in T; fname: in String; append: in Boolean) is
6_13 
		local: aliased T := tval;
6_14 
		--for local'Size use T'Size; -- this would be static as required only for scalar types
6_15 
		data: aliased Octets_Buffer_Pkg.Elem_Array(0..sz-1);
6_16 
		for data'Address use local'Address;
6_17 
	begin
6_18 
		Write_Octets(fname, data, append);
6_19 
	end Write;
6_20 

6_21 

6_22 
	function Read(fname: in String) return T is
6_23 
		local: aliased T;
6_24 
		data: aliased Octets_Buffer_Pkg.Elem_Array(0..sz-1);
6_25 
		for data'Address use local'Address;
6_26 
		ptr: Octets_Buffer_Pkg.Elem_Array_Pointer;
6_27 
		fsz: size_t;
6_28 
	begin
6_29 
		if not Exists(fname) then
6_30 
			raise Failed_IO with "inexistent/inaccessible file " & fname;
6_31 
		end if;
6_32 
		fsz:= size_t(Size(fname));
6_33 
		if fsz < sz then
6_34 
			raise Failed_IO with "file " & fname & " too short (" & fsz'Image & " vs expected " & sz'Image & ")";
6_35 
		end if;
6_36 
		ptr:= new Octets_Buffer_Pkg.Elem_Array(0..sz-1);
6_37 
		Read_Octets(fname, ptr);
6_38 
		data := ptr.all;
6_39 
		Octets_Buffer_Pkg.Free(ptr);
6_40 
		return local;
6_41 
	end Read;
6_42 

6_43 
end MMap_IO;

eucore/src/base_layer/src/mmap_io.ads

7_1 
	-- DC, 2023
7_2 
	-- memory-mapped I/O for types that are explicitly and fully specified with representation clause given that they are read/written as raw octets directly memory-mapped.
7_3 

7_4 
generic
7_5 
	-- any type but the user of this package is responsible to ensure that a direct memory mapping works correctly and reliably for T both ways and at all times
7_6 
	type T is private;
7_7 
package MMap_IO is
7_8 
	-- write the value tval of type T to file fname either appending or overwriting (append=false)
7_9 
	-- this creates the file if/when needed
7_10 
	-- raises exception on error
7_11 
	procedure Write(tval: in T; fname: in String; append: in Boolean);
7_12 
	-- reads one value of type T from given file fname
7_13 
	-- raises exception on error (eg inaccessible file, wrong size, failed mapping etc)
7_14 
	function Read(fname: in String) return T;
7_15 

7_16 
	-- reads the idx-th value of type T from file fname
7_17 
	-- function Read(fname: in String; idx: in Natural) return T;
7_18 
end MMap_IO;

With the above done, the actual writing and reading of rsa keys was actually *simpler* than it was when relying on direct_io and the likes. As a result, the udpated code is shorter by about 30% – the relevant keys_io.adb implementation went from 471 lines when using Direct_IO to 152 lines when using my own MMap_IO and this doesn’t even take into account the dropped dependencies and how much additional code they brought in.

For anyone interested in using the client whether now or at any future date, the good news from the above is that the previously low hanging fruit of switching client i/o to a more straightforward non-gnat implementation is now even lower hanging, seeing how there is the MMap_IO package to rely on and/or to use as example for the case where something slightly different might serve better. To further help in this vein, note that the client still makes use of Sequential_IO mostly for the ‘torrents’ part, meaning the read/write of files obtained at run time from the server – essentially game assets of all sorts.

Possibly a slightly different package would serve better to replace that use for game assets since the MMap_IO package isn’t aimed specifically at sequential i/o – one might perhaps make a generic package receiving both the file and the type desired, reading the whole in memory and operating from there. It would certainly be a faster and more reliable solution that GNAT’s imagined ‘control’ over multiple simultaneous accesses to the same file on disk. I’ll leave this for another day though and possibly, even preferably for another person, too, so feel free to pick it up if it’s of any interest to you.

If you set to work and have something to show or to ask, I’ll be happy to hear of it as well, so use the comments below confidently, there’s nothing to lose for it for sure.

  1. Perhaps it’s truly very naive to trust this way but at times it is also unavoidable – what you don’t know yet, you have to take on trust, there is no third way available. At best, you get perhaps the choice to trust a person based on what you know *of them* but at worst, as in this case, all you ‘get’ is to trust an unknown entity, pretty much, the ‘code itself’ aka all 360M of it or an ‘organization’ aka Adacore with all its changing history and multitude of people that you can’t ever know or get to know in any meaningful way anyway. So you trust, if and when you must but hopefully you then work as well towards reducing your own need for such blind trust. As you learn more, it’s worth remembering to revisit, reevaluate and correct or adjust misplaced trust, where necessary – this is exactly what this article describes and documents, my reevaluation and correction of previously assigned trust, as I got to know better what it was exactly that I ended up relying on.[]
  2. Especially since on review, I realised that even Direct_IO and Sequential_IO packages from GNAT *still* rely on the ugly and gnarly “streams” even though they avoid the broken ‘finalization’ and although there is both no need for such reliance nor any use for it. The very code in question states clearly in places that the streams abstraction is meaningless for the task at hand but nevertheless it’s brought in and adhered to because of the need to fit a predefined form, a need of comformance and uniformity across the board basically, that’s it. For instance, directly from s-direio.adb:

    — The following is the required overriding for Stream.Read, which is
    — not used, since we do not do Stream operations on Direct_IO files.

    In other words, it’s most likely a deeper issue at core – the GNAT implementation values uniformity and ultimately fungibility above everything else as it sets purposefully to shield the user from the requirement of deeper understanding and actual grasp. It’s a perspective entirely at odds with my own and so it’s no surprise its fruits are unpalatable to me.

    []

  3. Compounding dividends yet again, see?[]

March 21, 2023

Will I Need to Run Linux for Eulora2?

Filed under: Coding,Eulora — Diana Coman @ 11:09 am

Since the question in the title comes up with some regularity, I’ll write out the answer here for everyone to see and for myself to have it at hand when next needed:

It’s most likely for your own sake that you’ll need to run some sort of Linux, especially if you never ran it before, but otherwise sure, Eulora2 can serve as a good motivator for it, too.

To elaborate on the above, note that *all* you need to run Eulora2 is an environment where you can compile C, C++ and Ada code from scratch 1. Arguably, any and all operating systems are capable of such a basic feat as this, since it is, quite literally, what “operating” 2 a computer system is supposed to be about – making it process (“compile”) written instructions (“code”) into a form (“binary”) that the machine can then follow (“run”) to do exactly what you told it to do. And if you don’t like what it does or you change your mind or you simply want to play around and tell it to do something else, all you need to do is to tweak the code and have the machine process it all happily again, as many times as you please. Hence the importance of *all* the steps in that process, not just the last one, not just “run this”, but absolutely all of them, from first to last.

Does your current operating system allow you to do all and any of the steps above, routinely and easily? Then you don’t need anything else and you are set to go, nobody will mind you running Eulora2 there or anywhere else and you’ll find running it just as easy as running anything else. Moreover, I’d be very glad to hear from you how it goes, what you are running it on and what changes you are making to it, too – after all, it’s meant to be *your* client to play Eulora2, not in any way someone else’s.

If your current operating system doesn’t really make it easy to even know all that much, let alone perform and control one or several of these basic steps, then you’ll probably need to try out something more useful. What exactly might that be is entirely your choice but it is indeed more likely to be some version of a unix-based system. And if you truly want to have an easier time compiling and running everything as well as perhaps changing it to better fit your needs at some point, I suggest you pick an older version and never ‘upgrade’ it with all the bells and whistles that might or might not do anything useful for you but will likely break the toolchain and leave you stranded while also eroding to various degrees those hardly ever mentioned attributes of access, control and consistent return on your own learning efforts 3.

There is no reason at all and no intention even for there to ever be just *one* client for Eulora2 or otherwise put “the” client. Currently there is indeed only one, for the very simple reason that I made it and nobody else made theirs. As a result, my client is and will remain the reference client but it’s still not meant to remain the only one. Quite the opposite, I’m open to any number of developers and/or maintainers of clients coming forward to make and promote their own versions, getting paid for it, too. The way I intend to set it all up, developers and maintainers of clients will get paid for their work based on how many users choose to run each developer or maintainer’s client and for how long, as all this information is directly visible from the serverside. While this is still some way into the future currently, count this as the heads up on it – you won’t be able to say later you didn’t know it or didn’t have time to get ahead of others with it.

As for the reference client that I made and maintain, I would recommend currently that you run it on one of Debian 8 (Jessie), Ubuntu 10, CentOS 6 or Gentoo 4. This is not because the client “requires” these or it can’t otherwise run in a different environment but simply because it is already known to run smoothly directly out of the box with as little effort as downloading and starting *one* single script that does it all. Of the four versions mentioned, I don’t have any particular favorites, as they all have their own specific pluses and minuses, but I’d say they are all fairly easy to try out, too. Depending on your machine though, you might want to pick one that plays more nicely along something else and possibly out of the list above, it’s Ubuntu that is more likely to do so.

As mentioned previously, currently the entry point for Eulora2 remains through interaction with those involved, on this blog or on others. And there is help available as well, just read around, follow the links and you’ll find both the people involved and ways to interact – it’s the meta-game, if you wish, happening in plain sight, even if it seems invisible to some. If it’s not invisible to you though, just make and pick your quest(s), explore it all leisurely and when you’re ready, start commenting and interacting with people, too, it helps. In short, ask and you’ll get an answer. Better yet though, ask a good question and you might just get more of an answer than you even hoped for!

  1. More precisely, this means specifically that gcc 4.9 runs fine and the whole toolchain is not broken, either.[]
  2. For all the quotes, operating computers is not even all that far removed from other types of operating that may be more familiar, since they all come, of course, from the more direct physical operation aka use of tools of all sorts – after all, computers are merely more complex tools, basically tractors for plowing the information bitfields, nothing else. For this same reason, operating computers has in common with operating any other tool further requirements that don’t get mentioned explicitly nowadays all that often, most notably the ones related to requiring some amount of study that actually pays off and offering full access and control over all its parts.[]
  3. Convenience that caters to your lack of knowledge *always* costs more than is directly made visible and its hidden costs are quite often the ones that you’ll regret paying in the long term. Yes, it gives you a boost in the short term – quite often “now”- and it robs you of ~everything in the long term. At the very least it gives dependency, too. Just like any other drug of choice, you know?[]
  4. For Gentoo, there is no versioning possible even, since it’s always and by design a sort of mix-and-match, so you’ll need to pick your versions of everything in there. There is however a neatly written summary of a successful install of Eulora on Gentoo, so you might find some help with it, if you ask there.[]

February 17, 2023

How Are Things Moving in Eulora2?

Filed under: Coding,Eulora — Diana Coman @ 2:41 pm

Promptly and on demand, that’s how things are moving in Eulora2. To wit, one day in Eulora2, the discussion turned suddenly and quite literally to the skies above and whether the speakers knew it or not, the demand was thus made and fully heard:

Diana Coman: what, does that sky look flat to you?!
Vivian Sporepress: haha
Vivian Sporepress: Diana Coman: certainly not flat, possibly not even any sort of crystalline sphere; it seems to have some axial tendencies, perhaps an ellipsoid; and I have no hypotheses yet for the nature or causes of its cloud patterns though they appear unchanging to the unaided eye
Vivian Sporepress Diana Coman: ah, the ‘axial tendency’ looks more like a bilateral symmetry across a vertical plane, suggesting either a peculiar optical phenomenon or a decidedly nonrandom process behind the cloud formation
Diana Coman Vivian Sporepress – well, the clouds have to fit the sky’s …geometry and that one is indeed quite regular
Vivian Sporepress Diana Coman: the geometry of the sky might not be possible to infer with the present euloran technology level, for instance a sufficiently large cube shaded just right can be indistinguishable from the inside from a sphere!

Finding itself under such interesting scrutiny, the sky clearly took notice. Only a couple of weeks later, among many other improvements and developments, there was also this confirmed change:
skies_1_640.png

To be fair, there are quite a few other movements of things and of people happening at the moment in Eulora2, even if very little of it made it on to the blog. As always though, movement is the hardest to capture in pictures so the best way is to be there to see it happening as it happens, perhaps finding ways to be an active part of it as well. Second to being there, read along and comment underneath, perhaps, as I’ll aim to let it spill on to the blog in bits and parts at least from time to time, why not. There’s certainly plenty interesting and ready to move in there, from the multiple moons orbiting around Eulora2 and their impact on the grounds below, to the more prosaic issue of reconciling local movement with server’s view of the same.

The best part about it all is that the solutions, when found, turn out to be so easy to implement currently, given the supporting environment and a very much cleaned client code that it really doesn’t take long for something new to become visible in game once articulated clearly and meaningfully enough. This might change in the future perhaps but for now it is exactly so and turning gradually into a lot of fun, too.

How many other games do you know of, where truly all it takes to make a visible change is to speak up convincingly enough, without having to argue for it, without even having to frame it as a request? It’s enough to make sense and to pick at an interesting problem, that’s all.

December 19, 2022

Chat and Eulora2’s Communication Protocol

Filed under: Coding,Eulora — Diana Coman @ 1:07 pm

This article builds up on the existing, well-iterated specification of the communication protocol, aiming to detail the new developments and to provide a place for current and relevant discussion going forwards. For the core structure of the protocol though, the linked article remains the reference and although the discussion of that part moves out of necessity here as well, I see no need at the moment to copy its content too. If your question concerns the parts that are not directly described here, simply link precisely the part you are talking about, as needed.

The first further addition to the communication protocol enables chat in Eulora2 and is currently implemented and fully working. Worth making it explicit and right from the start that the chat in Eulora2 is by default encrypted 1 whenever in transit and effectively authenticated as well, as it fully benefits from the whole existing infrastructure supporting the game’s world 2. The WoT layer that is currently in the works will further provide the means to seamlessly keep out any automated noise, spam and the like while simultaneously enabling all users to effectively retain both control and accumulating benefits for everything that they put into participating. To the extent possible, I fully intend to further align the incentives as much as I can to support meaningful conversations that are worth re-reading, too – without any unnecessary restrictions and with fully owned responsibility as well as benefits for each participant, too. It is to my eye the only way in which having a chat capability is even worth it in the first place.

To add the chat capability to Eulora2, there are two new message types added in section 4 (Serpent packets), to request and receive chat lines, respectively, as well as one new action type added to section 7 (Character actions) describing the “Say” action. Using the same specification format, the newly added sections are the following:

4.9 Line Request

  • uint8 (type ID, =12), followed by
  • uint8 (count of lines), followed by
  • n times uint32 (id of chatroom), uint64 (lowest line number requested), uint64 (highest line number requested), followed by
  • padding to Serpent-message length

4.10 Line Info

  • uint8 (type ID, =13), followed by
  • text (line content), followed by
  • uint32 (chatroom ID), followed by
  • uint32 (speaker ID), followed by
  • uint64 (line number), followed by
  • uint64 (keccak hash of the line’s content and number), followed by
  • padding to Serpent-message length

Updated 30 Oct 2024: ratings are now fully integrated and used in-game, being fully handled via the relevant Rating node in the data hierarchy. Consequently there is no need anymore to have dedicated message types for discovery and retrieval, so the “rating request” and “rating info” message types below are removed entirely. See in any case the protocol specification for the full protocol.

4.11 Rating Request

  • uint8 (type ID, =14), followed by
  • uint8 (count of requests), followed by
  • n times uint32 (source ID), uint32 (target ID), followed by
  • uint16 (message count), followed by
  • padding to Serpent-message length

4.12 Rating Info

  • uint8 (type ID, =15), followed by
  • text(rating comment 3), followed by
  • uint32 (source ID), followed by
  • uint32 (target ID), followed by
  • uint8 (rating value 4 ), followed by
  • uint16 (message count), followed by
  • padding to Serpent-message length

7.9 Say 5:

  • uint8 (type ID, =9), followed by
  • text (line content), followed by
  • uint32 (room ID), followed by
  • uint64 (keccak hash of the line’s content)

7.10 Rate

  • uint8 (type ID, =10), followed by
  • text (rating comment), followed by
  • uint32 (target ID)
  • uint8 (rating value)

A line of chat in Eulora2 has a maximum length of 1445 characters. This is fixed and unchangeable as it is the maximum that fits in a 4.10 message (which has the least space for the line’s content out of all the messages relevant to chat lines and therefore effectively decides the *maximum* allowed line length). This limit is unlikely to be perceived all that often in practice but even if it is, note that it’s anyway more generous than most other existing chat protocols, irc included. Any client implementation is however entirely free to take its own approach as to how it handles this – whether it splits some user input into several lines if needed or whether it restricts input to this maximum of 1445 characters to start with, to give some concrete examples.

Beyond the above specific characteristics, for all intents and purposes chat rooms in Eulora2 are otherwise like any other game entities and thus the above additions to the communication protocol itself are both minimal and all that’s truly needed for a practical, working implementation. The speakers themselves are also characters (whether player controlled or not) in the game, hence fully described otherwise through the existing messages.

Messages 4.10 (Rating Request), 4.11 (Rating Info) and 7.10 (Rate) provide, respectively, a way to request and obtain from the server any desired ratings between two known entities in the WoT and to register/update one’s own rating of another. The comment part of a rating is meant to provide some context for the chosen value but as the value itself, the actual meaning and use is up to each individual and likely to be different from one person to another.

Worth noting that messages 4.10 and 4.11 are provided mainly as a basic and potentially last resort means of requesting and obtaining specific ratings. They are however quite unlikely to be the best choice for exploring or obtaining the full set of existing ratings, especially across a relatively large WoT, since each rating will require its own message and the traffic thus generated is to be paid for by the requesting client. A much cheaper and more convenient way to explore and update a local copy of the WoT will be provided in-game through an abstract object providing the full representation of the WoT in a single file and taking thus advantage of all the existing infrastructure, without requiring any new types of messages or other changes to the existing protocol.

Feel free to ask any relevant questions in the comments section below.

  1. Serpent keys[]
  2. Obviously, any of the users can otherwise make public the chat logs they have when and as they see fit but if you rely on something outside Eulora2 for that, it’s strictly between you and that provider, nothing to do anymore with either Eulora2 or the support and protection it provides.[]
  3. Limited to at most 1445 characters, matching the maximum length of a chat line.[]
  4. Valid rating values are integers between -10 and 10. For a given rating value here GR, the corresponding actual rating value RV is therefore calculated as: Min(GR, 20) – 10.[]
  5. Note that this is an action and as such just a part of a 4.5 message, Client Action, where the overall message structure is specified.[]

November 16, 2022

The Client for Eulora2 in Glorious Detail (and Picture!)

Filed under: Coding,Eulora — Diana Coman @ 4:38 pm

~Referencing, of course, the previous version of EuCore and even that very beginning of creating a useful structure to it all.~

As I’ve opened up a test environment for helpful people to give a go to the latest client, they are likely to benefit as well from an updated and more comprehensive overview of the full client code, as an initial map of sorts, at the very least. So this is what I’m aiming for with this article – to provide an up to date map of the current client and a place for questions and answers on it all, in the comments.

While the previous article focuses exclusively on the EuCore part, meanwhile I really had to deal as well with the GUI part, as there wasn’t anybody else doing anything about it otherwise. So the GUI part got significantly trimmed, mostly overhauled and otherwise changed from inside out to provide the sort of flexible and reasonably structured functionality that I need as opposed to the assumptions-ladden and spaghetti-shaped sort of “service” that was previously available. As a result, out of all those half a million lines of code and countless files, there are now… 40308 lines of c/cpp code in total remaining. More than half of this (27584 lines according to a wc run currently) is in fact the only remaining sprawling mess that got now at least insulated in its own directory, namely the PAWS horror. In more practical terms though, there are 11 classes that are almost entirely re-written and make up the bulk working part of the GUI, 2 classes (one entirely new and one adapted) for providing the whole set of views, windows and overlays that Eulora2 needs and otherwise a bunch of the remaining “utility” classes that are quite often simply dragged in by various spidery includes of the PAWS variety. Here’s the full remaining tree of the GUI (ie the C/CPP part of the client):

~/client20/src/
├── client
│   ├── err.cpp
│   ├── err.h
│   ├── euaction.cpp
│   ├── euaction.h
│   ├── euloader.cpp
│   ├── euloader.h
│   ├── eumovement.cpp
│   ├── eumovement.h
│   ├── globals.h
│   ├── meshattach.cpp
│   ├── meshattach.h
│   ├── paws
│   │   ├── gameviews.cpp
│   │   ├── gameviews.h
│   │   ├── pawsborder.cpp
│   │   ├── pawsborder.h
│   │   ├── pawsbutton.cpp
│   │   ├── pawsbutton.h
│   │   ├── pawscheckbox.cpp
│   │   ├── pawscheckbox.h
│   │   ├── pawscombo.cpp
│   │   ├── pawscombo.h
│   │   ├── pawscrollbar.cpp
│   │   ├── pawscrollbar.h
│   │   ├── pawsframedrawable.cpp
│   │   ├── pawsframedrawable.h
│   │   ├── pawsimagedrawable.cpp
│   │   ├── pawsimagedrawable.h
│   │   ├── pawslistbox.cpp
│   │   ├── pawslistbox.h
│   │   ├── pawsmainwidget.cpp
│   │   ├── pawsmainwidget.h
│   │   ├── pawsmanager.cpp
│   │   ├── pawsmanager.h
│   │   ├── pawsmenu.cpp
│   │   ├── pawsmenu.h
│   │   ├── pawsminimalslot.cpp
│   │   ├── pawsminimalslot.h
│   │   ├── pawsmouse.cpp
│   │   ├── pawsmouse.h
│   │   ├── pawsnumberpromptwindow.cpp
│   │   ├── pawsnumberpromptwindow.h
│   │   ├── pawsokbox.cpp
│   │   ├── pawsokbox.h
│   │   ├── pawsprefmanager.cpp
│   │   ├── pawsprefmanager.h
│   │   ├── pawsprogressbar.cpp
│   │   ├── pawsprogressbar.h
│   │   ├── pawspromptwindow.cpp
│   │   ├── pawspromptwindow.h
│   │   ├── pawsradio.cpp
│   │   ├── pawsradio.h
│   │   ├── pawsselector.cpp
│   │   ├── pawsselector.h
│   │   ├── pawsstringpromptwindow.cpp
│   │   ├── pawsstringpromptwindow.h
│   │   ├── pawstabwindow.cpp
│   │   ├── pawstabwindow.h
│   │   ├── pawstextbox.cpp
│   │   ├── pawstextbox.h
│   │   ├── pawstexturemanager.cpp
│   │   ├── pawstexturemanager.h
│   │   ├── pawstextwrap.cpp
│   │   ├── pawstitle.cpp
│   │   ├── pawstitle.h
│   │   ├── pawstree.cpp
│   │   ├── pawstree.h
│   │   ├── pawswidget.cpp
│   │   └── pawswidget.h
│   ├── pscamera.cpp
│   ├── pscamera.h
│   ├── pscelclient.cpp
│   ├── pscelclient.h
│   ├── pscharcontrol.cpp
│   ├── pscharcontrol.h
│   ├── psengine.cpp
│   ├── psengine.h
│   ├── psmainwidget.cpp
│   ├── psmainwidget.h
│   ├── smgdata.cpp
│   └── smgdata.h
├── common
│   ├── placeholder
│   └── util
│   ├── command.h
│   ├── consoleout.cpp
│   ├── consoleout.h
│   ├── fileutil.cpp
│   ├── fileutil.h
│   ├── heap.h
│   ├── log.cpp
│   ├── log.h
│   ├── psconst.h
│   ├── pserror.cpp
│   ├── pserror.h
│   ├── psstring.cpp
│   ├── psstring.h
│   ├── psutil.cpp
│   ├── psutil.h
│   ├── psxmlparser.cpp
│   ├── psxmlparser.h
│   ├── README
│   ├── singleton.h
│   ├── stringarray.h
│   ├── strutil.cpp
│   └── strutil.h
└── placeholder

4 directories, 104 files

The main method is in psengine.cpp 1 and that starts and initializes everything needed, then enters the loop provided by the graphics engine and at the end cleans up whatever is left. The first initialised is the graphics engine, CrystalSpace and that comes with its own “object registry”, configuration and initialisation dances that have been however trimmed to a minimum and corralled into a single method (and a single, shared configuration file, too!) that is hopefully reasonably easy to read. After that, the Ada part of the client, namely the EuCore library (see below for more on its role) is given the green light to start and provided all goes fine, the next steps are to simply initialise the GUI logic that is in psengine itself and then to enter the CrystalSpace loop that runs for as long as the game is running.

The GUI part has the following main components:

  1. psengine – the central part that provides access to everything else and otherwise registers with the graphics engine to receive regular callbacks on events of interest. Such events of interest include each frame drawing and all user input. The callbacks on each frame drawing act effectively as a sort of heartbeat of the client as a whole and thus coordinating the different parts of the GUI. User input is generally received and then passed on to the relevant action handler (see euAction below) or to further potential handlers where relevant.
  2. pscelclient – a class 2 that is meant to keep track of and otherwise provide all logic and management related to game entities. This is one level above the graphics engine and acting as a sort of bridge between the data as obtained from EuCore and its translation into the graphical format that the user gets to see. So pscelclient uses what the graphics engine provides, asking it to create, move, change and delete any graphics in the game’s world to match the information it gets from the data available from EuCore.
  3. pawsmanager – meant to keep track of and otherwise provide all logic and management related to “widgets” aka the various windows, views and overlays that make up the game’s interface rather than its world as such. This relies especially on the various views and overlays that are specific to Eulora2 and are defined in gameviews.h/cpp. This has most of the original mess intact but mostly sidelined and otherwise the useful bits and parts wrote in. The trouble cleaning this up is that ~all the paws calls back home to this and quite often with hardcoded include and calls too so it really wasn’t worth yet my time to clear it all up on top of everything else.
  4. pscamera – a class that provides and maintains the available ways to view the game’s world as it is otherwise populated by pscelclient.
  5. controlsManager – a class holding all the mappings of keys and mouse buttons so that they work as set in the configuration file and otherwise allow the user to provide input and interact with the game.
  6. euaction – a hierarchy of classes that provide the required input, processing and output capabilities to enable the user to perform all relevant actions in the game. The central psengine initialises a full set of objects from this hierarchy and then simply passes on to the relevant one the request for execution with any parameters as provided by the user at that point.
  7. euloader – a static utility class providing ways to create on the fly any graphics entity of the required type and using the assets indicated simply by a path. This includes creation of very simple elements such as textures but also quite complex ones such as animated characters that require a rather long list of calls to fully make when starting with just a bunch of files. While the creation of such graphics is quite tedious, at least it’s enough to have it packed this way once and then it serves endlessly, effectively providing useful defaults for all the “parameters” that aren’t actually needed or even known at start. The main user of this class is pscelclient.
  8. smgdata – a static utility class that imports all the Ada methods made available by EuCore and wraps them up for transparent use by CPP code from anywhere else in the GUI. Basically all requests to EuCore are made through this class.
  9. While the above GUI part focuses exclusively on the interaction with the user, it relies on the separate EuCore library to communicate with the server and to exchange and obtain all required data. The EuCore library handles everything in this respect, including creating a RSA-based identity and account, initiating and maintaining fully encrypted communication with the server, obtaining and updating all data related to the game’s world as available at different times to the player, retrieving from the server through the same encrypted communication any missing files and managing existing local files including their use as temporary defaults when needed and where appropriate. The full EuCore weighs in at about 27000 lines including tests and the mpi part that has about 8000 lines of code by itself. Here’s an updated full picture of all Ada package dependencies:

    eucore_2_1024.jpg

    Compared to the previous version, there is a striking change at the top where EuInterface is now the “entry” package instead of EuCore. This reflects actually better the meaning of the two packages and by now the whole code matured enough so that the refactoring was quite obvious, too. Basically EuCore is the starting package for the whole library, while EuInterface literally provides an entry point for any C code that wants to use the library and this includes now the previously missing C-compatible wrappers for EuCore’s Start/Stop methods themselves. Thus any Ada code that uses this library can entirely discard EuInterface and just start with EuCore, while any C code using this library goes entirely through EuInterface and doesn’t need to see or know of anything else.

    Further comparing with the previous version of EuCore, there are also a few new packages, most notably Mgm_Cache that comes now between Heartbeat and EuCache, as well as C_Buffer at the very bottom. The C_Buffer package is literally for interfacing with C code so that Ada code can take advantage of faster writing to files mostly. The Mgm_Cache package does what the name suggests, namely provides the higher level logic to oversee the data cache that is EuCache itself. In fact, the Heartbeat package serves as on overall coordination mechanism across the whole of EuCore and as such it effectively provides regular calls as required to various parts such as the communication link (Comm_Link), the data cache (EuCache) and the assets downloader (Torrents). As the communication link is both the most critical and the most demanding component of these, the Heartbeat really grew first of all as a manager of Comm_Link and only once that was fully working the rest could be developed as well. At the moment, the management of EuCache grew big enough to make sense to extract it in a separate package of its own but otherwise I don’t see a lot of benefit in doing the same for Torrents for instance.

    Note that all dependencies in the graph above remain hierarchical as they should always be. The automated layout of the graph tends to make it look as if there was some nonsense going on perhaps with that seemingly horizontal line going from OAEP to Keccak, but that’s just an artefact of positioning. In reality, Keccak is on a lower level than OAEP, being simply a hashing utility, although one that is most useful indeed. At any rate, OAEP uses Keccak and there is no dependency going the wrong way, as Keccak itself uses only raw_io and raw_types.

    Hopefully this gives enough of an overview to have some idea as to what is where and how the whole fits together. If you have though further questions on it, feel free to ask in the comments below and I’ll gladly answer.

    Finally, if you made it all the way here and would still rather see a less schematic picture as well, here’s a fresh one of the client in action at a meeting of like-minded creatures of Eulora2, surprised as they were by all the screenshooting commotion:

    eucore_1_1024.jpg

    1. Despite the name of the file and of the class, there is very little indeed left in there from the original code. I suppose I could have changed it all to euengine or something more appropriate but given how it’s one of those included from just about everywhere due to the original “design”, I didn’t really consider all the resulting noise worth the trouble.[]
    2. This is also mostly re-written with only very little indeed remaining of the original “gem” pain. It could still do with a haircut at least, I’d say.[]

October 19, 2022

The Needed Cut

Filed under: Eulora,Lyf — Diana Coman @ 9:30 pm

More than a year after the sudden death that left so many things up in the air, it’s time to make a straight cut to let all these things go and take stock otherwise of what is rather than what it might or could have been. So I’ll cut through the tangles as straight and direct as I can see it and as deep as it has to be, to get for my own needs to some clarity at the very least.

As far as I know, both Minigame and MPeX were fully owned and entirely controlled by Mircea Popescu alone. I am not aware even after all this time of any provision made for any sort of action to be taken in the event of the owner’s death and so the straightest possible conclusion is that there is no change with respect to ownership. There is simply a drastic and irreversible change to the action capability of the owner bringing it down abruptly from 100% to 0% and that’s about the end of it. The owner remains the same but he simply isn’t active anymore and therefore isn’t able to answer queries about it, to take action over it or to make any changes to any of it, for better or for worse. As a result, Minigame and MPeX with all their assets are left basically frozen, neither closed down nor even bankrupt but simply owned by an absent man.

Of course, one can steal things even from a dead person and in this scenario, Minigame as well as MPeX might still change ownership at some later time for all I know. As far as I’m concerned though, I’d much rather make my own thing whatever that might be rather than steal someone else’s and whether they are alive or dead doesn’t make any difference to this choice of mine. I don’t think there is such a thing as “default inheritance” – if the owner didn’t set up any sort of inheritance provision while they were alive, then there is only theft left possible as means to transfer ownership to someone else.

I suppose the above is about as close as it gets to the exact opposite of that old saying that “you can’t take any of it with you when you die” – actually, you can take with you precisely and exactly what you fully and solely owned *and* animated. It turns out that what you can or can’t take is simply a reflection of the strength and extent of your ownership and authorship of it, not some sort of absolute at all. In other words, when one dies, one takes out of the living, ever-changing world everything that stops changing with their death and it doesn’t even matter whether they wanted or not any of it to stop too when their own life did. By contrast, what can’t be taken is anything and everything that still has either a life of its own or at least another owner who is still alive. Neither Minigame nor MPeX got to have either.

Worth noting perhaps that despite the more usual single-sided view that tends to focus solely on the survivors’ perspective, this cuts in fact two-ways. Yes, it means indeed that nobody inherits that for which no explicit inheritance provision has been made. It also means though that nothing will live on beyond the owner’s life if that same owner, while alive, didn’t make successfully any provision for its survival after their passing. Intentions on either side don’t come into any of this at all and it really is only actions that decide it and nothing else.

Such frozen ownership as Minigame and MPeX are currently under has, of course, quite terrible consequences for anyone involved with them and still alive since there is no closure of any sort and otherwise in financial terms, all money invested in any of it is as good as lost, all shares, warrants and any other claims are basically rendered worthless. Since I had invested so much in Minigame via both Eulora1 and MPeX, I also lost a significant amount. As far as money alone is concerned, it’s indeed the case that I’ve never lost before anywhere near so much even if I scramble to add up on the other side of the scales all the money I ever lost previously to any sort of circumstance at all. It’s just a whole order of magnitude above everything else added together and there is no way around it or out of it – the only way to move any further is through it. Onwards, then.

It can be very tempting and it’s certainly very easy indeed to pass the responsibility for this loss onto the absent man seeing how he can’t do anything anymore about it. Even so, I choose to retain instead in full the responsibility for my own loss because with the responsibility thus retained, there is also my own agency and my own integrity retained and I can therefore move on unencumbered even through and beyond such situation as this. Possibly moving on even wiser and at the very least more experienced, quite unforgettably so.

Beyond retaining agency and with it responsibility as they are inseparable, what makes it possible to go onwards through this sort of terrible outcome is precisely the even higher amount of non-financial investment that I have been making each and every day over these past years. Because it’s indeed money that is easiest to lose, being as it is an external token after all and furthermore, being invested itself into something external that wasn’t under my control. If all that I had invested was my money though, the financial loss would be the same but it would also be the full extent of the story, turning the entire situation itself into pure loss and nothing else, just a bleak negative with no redeeming point of light at all. But this is not the case here and the only reason that there is anything of value left at all for me is simply that I had invested quite a whole lot more than only my money: my effort, my work and my commitment over years and through it all, as difficult and even as exhausting as it got at times.

Of everything invested, the money is the only part that’s gone. By contrast, the effort, the work and the commitment are not and cannot ever be gone, paying their own sort of dividends in the form of accumulated and verifiable achievements, experience, knowledge, expertise and even concrete tools and whole environments that I made and over which I have indeed full authorship and ownership, as well. As a result, I can take any and all of it from here perhaps not necessarily towards the exact destination I had imagined when starting or even at any other time until now but certainly anywhere I want to move them further, if and when I choose to do so.

What comes next therefore is quite an open path or even set of paths, since the paths available will be exactly those that I make and then choose to travel on. Although this is not at all and by any stretch of the imagination a place where I ever aimed to be, this is nevertheless where I am. What is gone is gone but I still have everything I made myself and everything I learnt through all the relentless work these past years, through all the experiences of all sorts and through the very close collaboration that touched on a lot of things indeed, from direct and long term experience with complex systems development to philosophy applied to game design and quite a whole lot more besides. In concrete terms, I also have the fully working infrastructure that I built for Eulora2, the full knowledge and even written record of everything that went into it, as well as the supporting tools that I designed and built for the exact sort of collaboration that I care for, possibly including code development for sure but positively enabling much more than that.

More important and on top of the above, I also have the relationships I built during this time with people who I value and with whom I share now this experience, as well. With such deep roots and on such a solid basis as all this, all my options are therefore quite open moving on from here and the choice is fully mine, too, regarding which way to go and how fast or slow to move, at any time.

Even loss is, in the longer term, what one makes of it.

September 20, 2022

Leisurely Coding or Solving the Interesting Problems

Filed under: Coding,Eulora — Diana Coman @ 1:26 pm

The prize for having a clean environment (not merely code, nor even limited to code) is refinement of precisely the most intellectually pleasing sort: getting to uncover and solve interesting as opposed to tedious problems. In other words and as far as I can tell from direct experience, even coding really doesn’t have to be at all “like plumbing” if the sewers are properly cleaned and it doesn’t have to be sterile either if the willfully blind focus on idealia is discarded. In a reasonably clean – not perfect, nor even “best possible” – codebase 1, there is both space for experimentation (aka introducing new things) and support for refining existing solutions. Arguably, there is even space and support for literate collaboration but this does tend to grow more slowly being as it is a fully organic culture in its own right. There is time, there always is… until there isn’t.

The most recent concrete example of what made for me quite an interesting problem brings together knowledge representation, data caching, remote synchronisation over a lossy channel and even matters of time representation across different contexts. Then again, arguably one’s problems can only get as interesting as one is able to make them to start with so perhaps it’s all tailor-made indeed. Either way, I’m neither boasting nor complaining, merely describing so here’s an attempt to sketch out the problem in its context:

  1. The game‘s client has to obtain, maintain and represent (including graphically but not only in such form) as accurately as possible the knowledge of the game’s world as accessible to the player and as it continuously changes as a result of the player’s own actions and as a result of others’ actions or even simply the passage of time. In all cases, the actual changes are not known upfront nor automatically obtained – while the server decides at all times what and how is in the game’s world, it’s entirely up to the client to *ask* for whatever it wants, whenever it considers it needs it. Quite on purpose, there is no information pushed from the server to the client at any time and although all questions receive an answer, there is no guarantee that all answers make it back to the client in any given timeframe or even at all. Hence, the problem here is to find an effective way to find out as quickly as possible all that is new but also, arguably even more difficult, what has become obsolete! And possibly the most difficult part here is to even define what “quickly” means since there are at least several definitions of time itself in this context: the user’s own time, the time in the game, the time as kept by the client, the time as kept by the server. What does “quickly enough” mean and when is something “too old” or even obsolete? What is the meaning of “1 second” in the game-world, what is its meaning on the network and how does one reconcile these two?
  2. The communication protocol defines the rules for a hierarchical structure that provides effectively the support for representing all the knowledge mentioned above but this structure is an ever-evolving one and with no specific limits to either depth or breadth at any given time, beyond a basic “it’s always finite” guarantee. Moreover, any request comes with a cost and that’s not even limited to time – arguably the resulting traffic is costlier, both as it *is* going to be included in the bill the user pays in one form or another (most likely directly wired into game mechanics) and as it can easily have undesired consequences clogging the client’s communication pipe for instance or even… triggering undesired changes because yes, in some cases, merely asking for something means that something will change 2. Hence, the direct, brute-force approach of “explore the whole knowledge tree at regular intervals” is not much of a solution to anything or not a long-term one, at any rate. And if one doesn’t explore the whole of it then how does one figure out the holes in one’s knowledge, those things one doesn’t even know yet one doesn’t know?
  3. The desired graphical user interaction (GUI) clientside adds some potential troubles of its own since it’s quite difficult to provide meaningful immediate feedback to an action when its actual results require in the best case a confirmation from the server (when guessed correctly) and in the worst case a search for and figuring out of how the world changed (when the guess is merely incomplete perhaps, although it can even turn out to be entirely wrong). Sure, one can *hide* this and pretend that everything is known upfront, arguably making it thus more convenient or something of the sort but pretense aside, the reality of it is simply that the GUI is at best twice-removed from reality and as such always kind of struggling behind: the reality it can directly access is at best a local cache and that cache is in turn at best a fragment (perhaps already obsolete in places) of the remote world that is at all times on the server and ever evolving even there 3. So how can the GUI be as truthful and responsive as possible when providing the user with a representation of the game’s world, without limiting them either? Because the whole point of a GUI is to enable, after all and moreover, the game itself is quite on purpose as open as possible – the scope of what one can do depends on what they have already done, not on some predefined and fixed laundry list that some uppity GUI takes upon itself to enforce on behalf of the server.

The above is the overall shape of the problem and as complex or “that’s not how these things are done” as it may sound, the core of it is already solved and working in practice. What still remains to be done, is really the refinement of this core of a solution. How interesting and rewarding such refinement turns out to be or not depends mostly on the person asking and working on it, as always.

As for myself, I certainly have even more interesting problems to solve on the serverside so I don’t particularly intend to pursue more than absolutely needed anything clientside. The client is open source after all and it will only ever be as good or fit for your own purposes as you make it to be. Does this sound like a downside or an upside to you?

  1. It’s always improving though and the cleanliness really has to do with how supportive of improvements it is, not really with some perfection or best anything. First of all, lots of requirements, constraints and even actual extent of the problems to solve got clearer only with practice and the corresponding code reflects this. Second, I learnt a lot in the process too and this also shows – yes, the 2nd time one does something it will probably be better done than their 1st and the 100th time even better than that. Cleanliness though doesn’t mean nor require skipping the first step nor the first 99 – that’s just a way to get stuck and it’s still silly even if very common – but simply creating that path that enables all the steps that may still come. So no, there wasn’t nor is there some perfect anything, code included, to be “fully solved” or achieved in some entirely ideal form and then either worshipped or blown to pieces, depending on inclination. There’s only sustainable growth, gathering up momentum and opening up new possibilities as it evolves, that’s the best possible meaning of clean – it enables the growth of new animals and plants, not that of new fungi.[]
  2. Does it ring a bell, at all?[]
  3. Are you *sure* that this is all that different from the actual “reality” of what you see with your own eyes even when no computers nor games are involved as such?[]

June 23, 2022

All Tattoos Are Temporary

Filed under: Coding,Eulora — Diana Coman @ 10:13 am

Like everything else in Eulora 1, the tips greeting newly connected players brought new life to an old form, generally turning common and stale anything on its head quite irreverently and with great effect. Since some of these tips still get quoted at times, I’m publishing my list of them here for vicarious enjoyment and, possibly, for easy reference as well:

  1. Welcome to Eulora! May your stay be preposterously prosperous.
  2. Do not think of yourself as an ugly human. Think of yourself as a beautiful monkey.
  3. The early bird gets the worm, but the second mouse gets the cheese.
  4. Better late than pregnant.
  5. You should probably do the laundry…
  6. Silence is golden, but duct tape is usually silver.
  7. Time flies like an arrow, but rotten fruit flies fly like flies.
  8. Everyone has a photographic memory. Some just don’t have any film for it.
  9. Mr. Spirover maintains a positive attitude, even though he knows it won’t work.
  10. If the shoe fits, get another one just like it.
  11. You can’t have everything – where would you put it?
  12. All things nobody else wants come to those that wait.
  13. A fine is a tax for doing wrong. A tax is a fine for doing well.
  14. In Eulora, it does not matter who is right, but who is left.
  15. The best space shuttle is but a large pile of chairs for the head.
  16. Happiness is having a large, loving, caring, close-knit family in another city.
  17. Fear not your own death – just try to not be there when it happens.
  18. Learning is not compulsory. Neither is survival.
  19. There are no rules here – we’re trying to accomplish something.
  20. Oh, the Vinu will love you. There is no sincerer love than the love of food.
  21. Anything that is too stupid to be spoken is sung.
  22. The first rule of tinkering is to save all the parts.
  23. Some are wise; others are otherwise.
  24. The bonds of matrimony are like any other bonds – they mature slowly.
  25. When a NPC marries, it exchanges the attentions of many players for the neglect of one.
  26. The most you can hope for is ending up with the right regrets.
  27. Everyone started out with nothing. Plenty still have most of it.
  28. An unbreakable toy is still useful for breaking other toys…
  29. Does the name Pavlov ring a bell?
  30. The best way to lose weight is to eat naught but inspirational posters.
  31. Old eggs are generally rotten; old hopes are generally delusions.
  32. To find out just how windy it is on the hill move there to escape the damp in the valley.
  33. If you fail to spend enough time laughing at stupid children, you will spend all your time going to war with stupid men.
  34. Your worst enemy has always slept in the same bed as you.
  35. The minnow aspiring to be a whale was stuck in a puddle that did not aspire to be an ocean.
  36. The best things in life are actually quite expensive.
  37. All tattoos are temporary.
  38. The secret of teamwork is that none of us is quite as dumb as all of us.
  39. Hard work pays off in the future. Laziness pays off now.
  40. The power of accurate observation is commonly called cynicism by those who have not got it.
  41. By the time you swear you’re his, shivering and sighing; by the time he vows his passion is infinite, undying… better make a note of this: one of you is lying.
  42. A cynic is that unrepentant blackguard whose faulty vision sees things as they are not as they ought to be.
  43. The world keeps ending, but noobs keep showing up as if the fun just started.
  44. The large print giveth and the small print taketh away.
  45. The worst misfortune that may befal your enemy is a large following of true believers.
  46. Poverty is the sort of suspect virtue one can very readly teach oneself.
  47. Rejection of praise is sometimes humility and some other times holding out for better praise.
  48. It is comparatively easier to make heroic gestures than to abide by the results.
  49. When logic leads to any humiliating conclusion, the human reaction is to discredit logic.
  50. The strongest indication that you lost your way is to find a large majority in agreement with you.
  51. Out of the mouths of babes come bubbles.
  52. The best possible world is the one you can’t live in.
  53. True democratic feeling is reserved for drunken men.
  54. If you can make a woman laugh, you can make her do anything.
  55. What will retrospectively be called will always finds a way.
  56. Most people love because they tried hating and found it requires too much effort.
  57. The very essence of romance is uncertainty.
  58. Man may have discovered fire, but women discovered how to play with it.
  59. Thread carefully when the nude offer to sell you shirts.
  60. Love at first sight is common; love at last sight is comparatively rare.
  61. Love cautiously, enjoy vicariously, dream reasonably, tread lightly and die quietly.
  62. The trouble with being poor is that it doesn’t allow getting into any kind of trouble worth the mention.
  63. Gravitation is not responsible for your great tumble – after all it tried to prevent your climb every step of the way.

February 4, 2022

What’s so interesting about Eulora2?

Filed under: Coding,Eulora — Diana Coman @ 9:57 am

It has meaning. As it brings to life a fully working and fully interconnected environment where *nothing* is created out of thin air or without clear, mathematically defined causes and consequences, each and every action done (or avoided) in the game *means* something – in other words, it… matters! Yes, it matters and means something first of all within the game itself but that’s no hard limit in any way and moreover, even considered solely within the game, it still means way more than it might seem at a first glance. This is not by accident and what follows might give some solid pointers as to why and how, so read on.

It is practically infinite in more than one dimension. To give just a few examples 1:

  • Your exploration generates (on demand!) new maps and there is more scope for such distinct maps than you can cover in a lifetime. So step in and see the world expand as you explore further and further. As you explore, note also the resistance of the medium, not constant but related to you and to your actions, ever present and ever giving meaning to the choice of the next step, because your exploration in this game *is* creation and not just a meaningless stroll through the endlessness. How far will you go and what will you create?
  • Your crafting, exploring, fighting, child rearing and various other game activities generate new items (and this means all sorts, computer controlled characters included) and the scope for these, too, is wider than you can cover in a lifetime. While the potential items are practically infinite, it’s your actions that pick from this infinite potential some specific item and then bring it into being. If you don’t act then no item will exist at all and the potential will remain just that, a waiting but never manifested possibility. So what are you going to make and who will you meet?
  • Gains are potentially infinite, too, as they aren’t capped at any random value, nor forced into any sort of artificially diminishing returns model. The only limit, here as everywhere else, is the player’s own capability and otherwise the whole of what there is in the game, nothing else. How much can you gain and what is your gain even going to look like?
  • The intricate connections and complex underlying laws of the game’s environment are statistical in nature and never directly exposed but ever present through their effects – or more precisely said, through what you are able to observe of them and, moreover, how much you are able to make of what you observe at any given time. How much of it all will you observe, how well will you understand it and what are you going to do with that knowledge gained?
  • The game promotes creativity 2 and it rewards applied, practical learning with full freedom of choice as to direction or means. The game’s environment reliably provides feedback and the unbending structure to test your hypotheses (and your mettle) against, while allowing otherwise any and all approaches you can come up with. Who will you turn out to be?
  • The game provides opportunities for meaningful action well beyond playing. The only limit with respect to opportunities available is defined by the players themselves, as they can literally interact with the game in any way and to any extent they want. From building up their own town and governing it the way they see fit or setting up shop somewhere in game to making their own client or their own graphics to be used – whether for free or for pay – by other players, the game’s environment simply provides just the right amount of structure to support it all but no restrictions as to what “should be done” or not otherwise. What do you want to do and how well will you do it?

All the above combined mean that it is your actions, observations, data gathering and even client development or meta-game that increase both the richness of the game’s world and your practical, applied knowledge that is not even limited to playing the game itself. In this sense, too, the scope is wider than you can cover in a lifetime. In this game, like in no other but exactly as in life, you can never really step in the same river twice. No “walkthroughs” and no “cheat sheets” can ever “solve it all” (aka spoil it all) for the next player who is neither the exact same as the previous player nor in the exact same place or time as them, nor even interacting with the game’s world in quite the exact same way. Your character in game ages and dies, too, and their children may continue so that your own experience of the game is ever changing and your play literally writes the history, whether through small or big deeds, still writing nevertheless. What part of the game’s history will you write?

“And will this game happen?”

“By itself, you mean?”

  1. For more on it all, see for starters the S.MG category on trilema.com, the Eulora category on this blog, quite a few of the articles in the trolloludens category on trilema.com as well as various other articles that are quickly found if you follow the links.[]
  2. Creativity is, at core, a successful cross-pollination (or weaving if you prefer) of sorts, the result of making viable new links between matters that had seemed until then separate or at the very least evolving independently, each in its own direction. As such, discipline, structure and a solid knowledge of many different fields are the support and ally of creativity, not at all a barrier or the enemy as they might seem at a superficial look.[]

May 17, 2021

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

Filed under: Coding,Eulora — Diana Coman @ 10:44 am

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 mechanisms 1 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 file 2, 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 together 3, 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):

eucore_2021_1_1024.jpg

  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.[]

April 12, 2021

A Tale of Two Code Changes

Filed under: Coding,Eulora — Diana Coman @ 2:46 pm

This past week I worked on a significant refinement of Eulora’s data hierarchy, that core structure used to fully describe for the client the game’s endless, complex and ever-changing world. As it happens, this refinement was made possible as a result of a lot of previous work 1: having recently gained full control over the creation of all graphical elements as I successfully grew my own mountains alongside GUI elements and literally everything else involved, I was also finally able to break up into more useful pieces and ultimately get rid of that sore anomaly of an “Outdoors” object that had previously catered to the requirements of the now-discarded “terrain plugin”.

For the easy part in getting rid of that bulky and spurious “outdoors” object, I had at least already in place the natural building blocks that would get used, namely the CSModel object as well as the ways to position and even name it, if desired. Even so, a change such as this, touching as it does the game’s own way of structuring knowledge essentially, can never be a very easy thing to get right, as there are many different connected pieces that have to fit – at least to some working extent – at all times. So I was a bit apprehensive about the amount of work it might end up requiring in practice and I made therefore a padded estimate of the time it might all take.

At first, I made the needed changes on serverside, where everything is defined and created in the first place anyway. There was initially more work than expected, indeed, but mainly as a side effect, a sort of slightly hurried growth, rather than anything else: while the change to the hierarchy itself was quite straightforward, the details pushed into being other parts that weren’t yet quite ready otherwise, although they were somewhere further down on the list for later, anyways. So I took more time than I had initially estimated to design and then add the needed embryos of these parts too, even though they still miss a lot of other parts that they in turn require and as such, they are sure to further change as they develop. This is expected and unavoidable though – it’s how everything else on serverside currently came into being as well, after all.

As the serverside work stretched even beyond my padded estimate for it, I was increasingly thinking that I’ll have no choice but to revise the overall estimate entirely, as the remaining time for the whole clientside work shrank and shrank. But then, when I finally got to update the data hierarchy in my own EuCore component on clientside, there was indeed very little required: simply the addition of the new object types, the deletion of the old one and a few updates (flagged automatically, too!) to take them as well into consideration when looking for broader categories such as “graphics vs non-graphics objects”. The rest of it – and moreover, the *more significant* and difficult rest of it simply worked and not by accident either: it was simply my previous design and implementation of both EuCore and its interfacing with the GUI saving me days of work now, as it handled entirely new types without skipping a beat and without requiring otherwise any change *at all*. How pleasant is this as code to work with and when exactly is the last time you encountered such ease when making significant changes to the code you are working on?

The second code change that made its way onto my list this past week was in principle a very simple thing to do, clientside entirely: as the water was now simply a CSModel like any other item in the world, it ended up nicely rendered but nevertheless surprisingly…solid, despite the data hierarchy otherwise holding enough information as to its “water” nature. In other words, there was clearly a need for the GUI part of the client itself to become a bit more discerning and create water as water (not solid and also reflecting things around, for instance). Since I had already written the code that successfully created water as such, this change was in principle just a matter of adding and using a flag, nothing more.

In practice though – in hindsight unsurprisingly, as it touched the remaining PS code – the simple change turned out to be not that simple and especially not as straightforward as expected: first, the water was rendered reflecting quite nicely everything around, so that it was clear that the discerning part worked as expected but… this “water” was still a solid, more like frozen ice perhaps than any water. Then it all got even more puzzling: the water was still solid *despite* my loader specifically *not* setting collision detection on it (in graphics terms, this solidity is simply a matter of whether one bothers to check /enforce any bumping into the object in question or not). Moreover, this unexpected solid was not even reliably solid but rather a weird half-assed sort of solid: one would walk on water indeed, for all the incredulity one might have as to the possibility of such feats but then, unexpectedly (a loss of faith, perhaps – whose faith, though?), one would sink deep into it and then… oh, then, one would get *stuck* in there, too, unable to move in any direction, if able nevertheless to spin as much as desired! Basically I had inadvertently created in there a sort of treacherous quicksands for all their watery looks otherwise.

Both the cause of and the resolution to the above unexpected wtf turned out to be of the same sort as previously encountered: the cause was a remaining bit of planeshift code and the resolution was to discard it entirely. Basically there was a spurious “psSolid” wrapping of the engine’s own collision detection object doing nothing other than getting in the way – it literally added *nothing* of its own other than the perpetrator’s name at the top of the file 2 and otherwise another indirection layer in the way of anyone trying to make sense of the code overall. On top of this, some other bit of remaining PS code *also* took it upon itself to decide and add this half-assed psSolid thing to *everything* that went through it, just like that. As a result, there was this funny situation where simply bypassing it all solved the problem entirely – as usual, not playing along with the stupid has this magical effect of making all sorts of problems just… disappear. So I further deleted some of the increasingly less but sadly still remaining PS code and as a reward for it, I had my water being water again, nicely yielding rather than solid and otherwise letting one freely move through it at all times, instead of randomly trapping unaware swimmers in it.

Having previously trimmed and trimmed again at these same planeshift tendrils, this latest trim was otherwise quite effortless too, basically a very pleasant and rewarding way of improving the code from all points of view. How is this for coding work – effortless code *reduction* for added benefits and increased functionality, too. How does this compare with your current coding work and experience as to what coding even involves, hm?

  1. This most recent work merely added, in turn, on top of full years of cleaning up all sorts of strata upon strata of a huge mess.[]
  2. And I have to say that by now I had even guessed correctly the exact “author” name in that file, despite there being quite a few names scattered otherwise around the CS and PS codebase, you know?[]

March 24, 2021

Growing My Own Mountains or the Plugged Off Plugin

Filed under: Coding,Computer Graphics,Eulora — Diana Coman @ 5:14 pm

Eulora’s legacy client requires no less than 12 image files to create just one sector of the whole wide world and this piles on, of course, the megabytes transferred and stored under the generic label of “art assets”. The reason for such shameless requirement is the reliance on one of those oh-so-flexible plugins that the engine (CS aka Crystal Space) is plagued with: the terrain plugin that supposedly generates programmatically all sorts, except in practice it still expects 5 detailed, pre-made maps, of which 4 (basemap, lightmap, materialmap, normalmap) are anyway calculated otherwise based on the 5th (the heightmap).

Perhaps you wonder just what does the plugin do with 5 maps anyway. Well, a deeper dig into the code of it all revealed that the plugin just uses them all to create over and over again, at each start of the game, the same terrain mesh – except it never quite calls it directly mesh (it’s terrain!) and it creates it according to its own rules and decisions as to what all these maps mean and how they are to be interpreted. Basically the terrain plugin expects that it owns the terrain entirely and so it gets to actually decide on what height is where (according to its own, internal rules, too!) and even whether the player can or can’t go somewhere – what representation vs meaning, what client vs server role? In this brave new world of no depth at all where everything that matters is how something looks, the graphical representation thinks itself the master of everything and so gets repeatedly in the way at all stages until the inevitable point where I finally give up on working *with* it at all and therefore ditch it entirely, just as it has happened before and as it will continue happening: if you keep saying you can’t do this and can’t do that and won’t do the other thing either, the result is not that whatever it was to be done doesn’t get done – it’s simply that it will not be done *with* you but rather *to* you and not in ways of your own choosing either.

Due to the above stupidity of the terrain plugin in CS, what started as an attempt to merely calculate the terrain’s final height at any given point, ended up in quite a bigger and more tangled chunk of work to do but the net result is the addition to my gfx pipeline of a direct, full creation from scratch of a terrain mesh that can then be sent to the client to use as it is – no need for sending 5 maps over the wire, nor for any further polygonizations and calculations of height each and every time someone fires up the client: at least if it has to use a rasterised format, let it be calculated just once on the server and then reused forever by all the clients that may ever need it.

As usual, things didn’t initially go very smoothly but that was quite expected: first the whole thing didn’t show at all 1, then the “object is not closed” complaint kept on complaining even on the closest of them closed objects 2, then the movement went haywire, whole mountains kept playing hide and seek with my view, bits and pieces started flickering, vanishing, blackening out or any combinations of those three. And then, finally, even the return to the “old way” (aka plugin based) failed, revealing at the least expected moment that the plugin itself was in fact no better at handling the sort of situations I was stressing my poor mesh with.

All the above out of the way and written here once for all the future uses, let’s illustrate it, too. First in line and as seen by my aptly (if algorithmically) named Perfectput Dot rabbit, the amazing whole land-vanishing capability of the terrain generated by the plugin – basically the culling itself fails just as much on my mesh as it does on the plugin’s creation:

Now you see it…
mesh_terrain_7_640.jpg

Now you don’t…
mesh_terrain_9_640.jpg

Set entirely at ease by the above as to failure being a given anyway, I started growing mountains out of the submerged plains, which was simple enough when all it takes is cranking up the maximum height parameter of my generator: the mountain grew and grew but it still remained stubbornely and quite ridiculously pointy:

mesh_terrain_1_640.jpg
mesh_terrain_2_640.jpg
mesh_terrain_3_640.jpg
mesh_terrain_5_640.jpg

While the last image above is at least not *that* pointy, finally, the whole exercise made it quite clear that there was some deeper trouble in there, as indeed there was: first of all, I had an error in my height calculations (so that the factor applied was not at all what I thought it was) and second but more important, it seemed to me rather silly to calculate the full heightmap of 1024 x 1024 size to then pick for use a smaller grid (to keep the size of the mesh file itself reasonably small). Basically the added detail that the heightmap held was just thrown away because the polygonization used *less* detail, not more (if it were more, I’d have interpolated and in principle it should smoothen it further so it’s possibly closer to what I’d want, anyway). So I tweaked a bit the code to simply add the actual scale and then generate the heightmap only as big as needed: a scale of 4:1 for instance means that real world unit is 4 for every 1 in the heightmap and therefore a real world map of 1024 x 1024 can be fully obtained with a heightmap of only 256 x 256 (since 1024/4 is 256). Otherwise just using every 4th value in the heightmap doesn’t really bring much benefit, as it’s not “more detailed” since the detail itself is just glossed over. In any case, this part might still need further tweaking but for an illustration, here’s the resulting landscape with max height set to 100, scale factor set to 8:1 (hence generated DS heightmap of only 128 x 128) and values used for DS seeding from -5.0 to 5.0):

mesh_terrain_4_640.jpg

For this initial testing stage, I didn’t bother with creating any actual texture for the terrain but I don’t really see why that would be any trouble really, whether doing splatting or anything else really – one big advantage of ditching the terrain plugin is that there is now no limit whatsoever on texture or materials, I can even mix as many materials as I want, no “exactly three” restriction anymore. At any rate, the images above simply use the “sand” material (both image and normals, hence a bit more than the plain textures used for my meshes so far) everywhere, with a plain mapping – while the effect in the distance seems reasonable enough to me even with this tiny material, the close up does indeed seem to require more detail. Possibly the current 512 x 512 texture is just stretched too much on the triangles of the terrain when close enough and since I don’t do any “lod” shenanigans, it looks for now as illustrated above. Nevertheless, this should be improved simply by using a better texture overall (and since that would be a single one rather than 3, it can even be 3 times bigger without any increase in size of the whole .zip as compared to the current one).

Worth noting also that in all the above illustrations, the water and the sky are unchanged – they are still created entirely client side, with just the texture and the normals sent by the server. I don’t see any reason to change this (both sky and water are fixed, regular shapes) but at the same time, having now increased (more than I ever wanted) my hands on experience with messing up meshes, I tend to think it’s probably worth changing on clientside the *shapes* in use, especially for the sky (and as a result, possibly, the texture to match, too): the current sphere is fine in a way but it creates this unwanted effect that tilting the camera at any point low enough to the ground results in one looking *under* the ground where there is a whole half sphere of… sky. Since “filling” this half sphere is apparently something just not working (because there is no depth to anything, no matter how defined), the alternative that I can see would be to have the world “container” simply as a cuboid with the terrain resting on the very bottom (so that there isn’t anything to look at underneath, no matter what tilting) and then simply add a half-sphere for a dome effect or otherwise, possibly even texture the corners of the ceiling out of sight.

All this opens up of course a whole new set of potential parameters and/or approaches for terrain generation but whatever parameters are chosen in the end, the pipeline still works the same so I’d say that the mechanics are now at least in place and the rest can then be easily tweaked further at any later time. What changes otherwise as a result is the “Outdoors” object in the hierarchy of Euloran objects since it looks set to simply contain a mesh for the terrain and then, perhaps, the materials for terrain, sky and water, unless those end up also given separately perhaps.

  1. While CS docs are clear enough that the “front” or visible face is the one with vertices given clockwise, the bit missing was that CS’s z axis points forwards! Noting it here for any future puzzlement on this topic: the “top” (or “far”) left corner will therefore have the highest z coordinate and the lowest x as opposed to lowest z and lowest x. The rest then falls easily into place, as one just literally needs to list the vertices in clockwise order. Worth noting also that a closed 3D shape with faces oriented so that they are visible from the outside will therefore have… transparent sides when seen from the inside. This is the illusion of 3D in computer graphics at its most cumbersome: there is literally no depth to any of it![]
  2. Oh boy, how infuriating this was: what the stupid thing checks for is not exactly whether the object is geometrically closed but rather a much more custom interpretation of “closed” namely “does this object have at least 16 triangles and in such case, does it also have all edges – exactly as given – shared between at least 2 triangles?” Specifically, this means that you can’t really easily define a 3D shape with different level of details on its different sides because you have to match the number of triangles wherever the sides meet, basically there’s no way to split one side into several smaller ones for added detail (even if geometrically the whole side is thus covered). In turn, this brought at first the horror realisation that making the terrain a cuboid as opposed to just a top sheet would then require effectively *doubling* the vertices and the triangles (which is not a negligible doubling when talking of a whole sector of the world). Nevertheless, in true open source spirit, it turned out that *ignoring* the whole complaint entirely is the best course of action – if it whines that even closed shapes are “not closed” then it will get fed fully open shapes since it’s basically not worth catering to. Funnily enough, this made otherwise no discernible difference in practice, at least not beyond the presence of the complaint itself in the console. Go figure, it turns out that I am *still* paying too much attention to what is nothing but empty pretense, sheesh.[]

October 4, 2020

The Story of Foxybot

Filed under: Coding,Eulora — Diana Coman @ 5:06 pm

Prelude:

~it’s ten minutes to set up and ten minutes of use to make itself indispensable. the chick can code, i don’t remember a tool becoming THIS FUCKING NEEDED in recent times.~

The Story

Going through my own archives and old logs may be time consuming but also quite an interesting exercise, meaning as it does that I get to look at an older context and a younger self again, only with more knowledgeable -and not just older- eyes. With the benefit of hindsight and of accumulated experience, even well remembered situations and well remembered words (of mine and of others) still yield more meaning than they did at that time, for what were *then* merely tiny signs and seeds meanwhile either grew and blossomed or shriveled up and died but looking back one can more easily see -in both cases- why and wherefore that specific outcome was even quite likely for each of them to start with. At times there are also the rather funny in hindsight bits, like those very, very early signs of my want of a bot:

diana_coman: I’m rather clueless at scripting unfortunately, did not have to use it much until now
chetty: there is a plan to allow crafting to proceed even if you are not online, queue up actions, but it is a way off
diana_coman: I will try to look into it, but not sure how much I can do 1

diana_coman: chetty, can you help with any pointers regarding eulora client’s structure if I am to start looking into the code? I have no idea of a crystal space app, so probably any ideas/pointers would be helpful
chetty: well what did you want to do with the client?
diana_coman: I first need to get some reasonable understanding of the code and what is done where, as otherwise I will hate just hacking it 2
diana_coman: but anyway, I was thinking of something along the lines of making annoying tasks less annoying 3
diana_coman: such as: automated “go to coordinates” 4
diana_coman: (I don’t even know how difficult that would be, lol)
chetty: lo, well the user interface pretty much all goes via ‘paws’
diana_coman: or “repeat crafting x until out of recipes/ingredients/etc”
diana_coman: or even shopping list thing for a given recipe
chetty: ahh some nice botting, that would actually be pretty cool
diana_coman: well, after playing it so much 5, I think I have some clear ideas of useful features, but I am still clueless regarding the client code, so not so sure how much/when I get to actually implement :p

diana_coman: for the moment more like trying to figure out the code with the hope to improve a bit the game experience afterwards; as in: automate repetitive tasks.
thestringpuller:reminds me of Eve
diana_coman: wasn’t Eve anti-bots?
thestringpuller: so is WoW but it didn’t stop people from using them
diana_coman: well of course, but as far as I understood Eulora is actually pro-bots
thestringpuller: tis like dinosaur island without the geneticism. (dumb facebook puzzle thing)
mircea_popescu: diana_coman eulora is actually pro. and ima write an article detailing it 6.

The above log conversations happened throughout June 2015. By the end of July 2015, I had released version 1.0 of my Crafting Bot. I still recall those weeks in the summer of 2015, of rather intense wtf at contact with the legacy client code. Not as much because they are the worst I know or even knew then otherwise but more likely because they packed quite a shock, seeing how at the time I had been happily away from code for years. So it was an unpleasant shock, a lot of unpleasant work and otherwise no help to be had from anywhere, but I still made my bot because I had a use and a need -in other words a real want- for it, as simple as that. Or as I was writing some time later: last year I started programming again, rather unexpectedly. Simply out of interest, as it happens with most things worth doing: I wanted something that did not exist, so I knuckled down and did it – as a result, now it exists.

From there on, things moved on quite quickly. There was indeed a prompt reward and publicity granted for and to my fledgling bot, as well as some various further discussions of it all on irc 7. At least one other player clearly stated they had a use for the bot too but if you think that actually helps concretely in some way… what can I say, it doesn’t really, no. At any rate, by September of the same year, I had released the second version that included a lot more but was also essentially a full re-write of everything: basically the first version served more as a prototype and ended up thrown away, the new bots re-written almost from scratch and all that on my own effort, my own time and my own dime, imagine that! Silly woman that I am to actually put in such effort when nobody is paying me for it and others are even stood -and waiting- to benefit from the results 8, right? Well, perhaps it all comes from my stupidity or perhaps it all comes from evaluating things a bit more thoroughly and from more angles and sides than you are used to even consider at all. At any rate, even laziness might have more sides than you think and for illustration of this, here’s the intro to this second version of the bots:

Like anywhere else, work in Eulora is a most wonderful thing …to watch. So rather than work on working, I worked on avoiding working – and [as a result] here are my two bots ready to take over Euloran world

By November of 2015, there was yet another version of the bots released and I can tell even now from my own writing that I wasn’t all that happy with all the muck and yuck involved in getting things done there. But nothing worth doing gets ever done without some struggle or without having to overcome unexpected shit so what is needed gets done whether it’s pleasant, easy, convenient, expected or exactly the opposite (as direction, while double, triple or many times over as intensity) of all four and more to boot.

The above is not to say of course that there weren’t also good things coming out of it – there were for instance the first actual maps of resources entirely *made possible* by and through my bot, even as early as October 2015. There was also the fun had in finally having a working systematic tool to use against the full bulk of unknown 9 that stood between myself and any significant gains in Eulora or otherwise put between myself and …actual playing (scratch that, I mean: winning) of the game! While it’s not documented as such at the time because I didn’t want to give it away 10, I can still recall that fine morning when I woke up to find that the tirelessly working bot had discovered an entirely new item and had produced already a very clear map that fully outlined the area of interest, as neat as neat can be. I’d say that’s about as close as one can come in this time and world to discovering new worlds essentially – if you think Eulora is less of a world for being made of bits, you might be missing yet again some sides and angles to it, so perhaps read a bit more about what Eulora even is and how it’s complex economy means way more than it may seem at a first glance or than the construct “real cash economy game” has meant so far.

At the end of 2015 and beginning of 2016, S.MG faced an unexpected blow of just about the worst sort possible: the CTO at the time and co-founder of S.MG fell ill and she didn’t recover. The announcement came as a terrible shock in more ways than my brief words in chan at that time might suggest. Then again, the #eulora chan or any other irc chan for that matter is not the place for everything, either. At any rate, this is the sort of event and resulting context for which nobody can really plan or get “fully prepared” in advance – when it happens you’ll face it with whatever you got to prepare *unknowingly* up to that point and whether that’s enough or not quite or not at all or only just there, it’s still all there is. In this case, all that useful work that I had put in before opened up simply a chance and nothing more, certainly not much choice: a chance for Eulora to restructure rather than close down and a chance for me to get involved even more than I had ever considered even possible by that time. Note perhaps that there’s little choice involved in this at all, more of a close and narrowly avoided encounter with the end.

As I started working for S.MG, there was of course less time to pour into developing the bot further. Nevertheless, its use only increased as players simply could not quite afford to *not* use it and there were various additions to it by myself (for instance, bundling) as well as by other players: first, Daniel P. Barron’s drunken explorer mode and then Mocky’s updates that were directly commissioned by S.MG though meanwhile vanished 11 together with their author.

The article on Foxybot’s 1st anniversary gathers together quite a few pieces of evidence regarding its usefulness and widespread use. And beyond that, there is of course a lot more that came out of it, not least of which a lot of discussions in the logs and a distillation of that into a whole model of software development, no less. While that specific direction didn’t *yet* get pursued much further (mainly for lack of time on my side, as I got swamped with a lot of other more pressing matters), it’s still precisely the sort of “unexpected” growth that marks as far as I can tell ~all things worth doing, worth *committing* for, in the first place – correctly or not, I consider that this is the best sign of having made an excellent decision as to what problem to solve: a good (or at least decent) solution to a random problem may solve that specific problem and no more but a similarly good/decent solution to a problem that *matters* offers in return a whole lot more than the initially visible direct benefit, as it practically opens up entirely new directions for growth.

The above theme of opening up new directions for growth is in fact very central to Eulora as a whole, I should say – and with good knowledge of it – by now. There’s the whole of the S.MG category documenting that for one perspective but meanwhile there are all the additional directions merely touched and barely opened up as part and parcel of all the work that went into getting some actual computer graphics and making the 2.0 client. While the old Foxybot is not going to make it as such into the new client (mainly as there’s little point to importing through it all the state-machine madness that was imposed by the rotten parts of the legacy client), the directions it opened are not lost or closed either and the automation of tasks will be just as needed some point anyway – only quite an easier task to implement with the new client and protocol.

Nevertheless, for all those openings and opportunities happening in plain sight, for all the documented amounts of Bitcoin involved and exchanging hands through the game and for all the previous opportunities not taken when available and closed meanwhile, those fewer and fewer remaining openings seem just as invisible today while available as the previous ones were back when they were still available, too. Arguably this is also exactly as it should be: the very same environment can seem empty and deserted to some and overcrowded with opportunities to others. At the end of the day, those seeing and capable do, those bright enough may at least follow and the rest will – at best – wait for whatever might happen since no happening has anything to do with them at all, anyway.

  1. Bwahahaha. How much did it turn out I can and did do and how much more there is to be done…[]
  2. Oh, the naivety of this young self of mine, how I wince to see it yet again! Did I get meanwhile a “reasonable understanding” of the unreasonable, would you say?[]
  3. This is how useful things start before it even registers as a start: by improving something that is, in its context, in its *meaningful* context. Not all that much by “being found”, nor by popping out of nowhere all formed and valuable by and in itself, objectively and in a void, no, how surprising. (For my English-speaking readers, here’s a translation of the highlighted fragment: The attempt to create value alone and in a void, apart and away from actual people and their interactions, the failure to understand that such a thing is not even possible since all the value comes from the subjective evaluation by others, the nutty search of some objective “gold” in the virtual world, those are all telltale signs that you still have all sorts of lessons to learn about the virtual as well as the real world and anyone having any money would certainly prefer, at least in principle, that you learn those needed lessons on your own money [rather than on theirs].) []
  4. The most important part of this aka pointing to the correct place was relatively easy to do, if extremely useful. It’s still in use: the /pilot command.[]
  5. In other words… after getting some actual experience and therefore beginning to form some understanding of the whole context too, as just the most basic prerequisite for coming up with anything useful anyway.[]
  6. Worth a read and a re-read at the very least, I’d say.[]
  7. The whole conversation there is quite funny for me on re-read. Then again, I suspect there’s at least one participant who’d rather prefer that the conversation and even its whole context never existed at all.[]
  8. Note that this *second* part is very important, too: while a lot of effort can be put into all sorts for which nobody is paying you, it does NOT follow that the lack of pay is a way to choose things or some sign of …usefulness! This is not something new, of course, nothing is new after all, but here’s a more straightforward statement that may help with it all (the loose translation to English is mine): in short, the fear and avoidance of competition is the mark of the incompetent and of those who know already that they’ll lose. The rest of people deliberately look for competition, focusing on identifying salient inefficiencies, coming up with strategies to get rid of them and then knuckling down to work. That’s what doing business even means, not just on the Internet but everywhere[]
  9. As part of the attempt to attack that, I also made and published a whole library of items that turned out very useful too.[]
  10. There is some data published at a later time and then even the actual fragment of the map, close to one full year later.[]
  11. Eh, except those I have mirrored myself, of course, as usual.[]

August 31, 2020

In Euloran Moving Updates: Inner Slots and Outer Wheels

Filed under: Coding,Eulora — Diana Coman @ 7:39 pm

I don’t care how many angels can dance on the head of a pin! What I want to know is how many tiny details can one devil simultaneously be in.

The only reason why my love for graphical user interfaces (GUI) suffered no loss over this past week or so is that I hadn’t any such love to start with. The clientside required bits and parts for performing actions turned out to be a considerable pile of small but quite detailed pieces that seem tiny each on its own but add up quickly to eat a lot of time while remaining otherwise hidden internals so that there is at first nothing to actually show for all the work. As I looked in more detail at what the GUI needed to be able again to fully interact with the world, the work seemed at first to just keep growing at every step instead of shrinking: there were GUI elements missing and then new methods required for querying the local cache and new configuration options and synchronizing and whatnot.

Experience helped here a lot, as I know this sort of pile-of-details stage for exactly what it is, part and parcel of work at times. So I just kept the work as contained as possible and otherwise I kept going with it until I finally brought it all to the point where there are enough of the required internal mechanisms in place so that they can support the desired GUI interaction itself. As a result, I can illustrate now at least some of the new additions, if still not all. At any rate, Eulora’s client has now in place the basic mechanisms for interacting via mouse or console (text) with any items or characters in the world, whether they are contained in one another or simply directly in the world, on their own. There are still all sorts of details that will have to be sorted out – or possibly altered even – as everything else comes together but those are secondary details, those that can never be fixed in stone at the first pass anyway, nor should they ever be so inflexible as to have to be fixed in the first place.

To start with, the Console has increased usefulness and usability. The most important improvement from my point of view is that there is now in place a generic mechanism to deal with any command from the user: the first word is the command itself, while any following text is read as a list of “parameter=value” tuples, where the order does *not* matter and there is no restriction whatsoever on what “parameter” might be. Basically the Console simply grabs the full text, uses the first word to find the action that goes by that name (if any) and then passes whatever text follows to the action itself to handle. The action in turn has its own default values for all parameters but then goes through the given text and sets the given values as found, if and when found. As an interesting effect of this approach, the user can even set the same parameter several times in a single command but the value used will simply be the last one encountered. Moreover, as much as possible the same letter stands for the same thing for *all* actions – the most obvious example is the target which is t everywhere, not like it’s all that difficult, really. Anyway, by means of illustration, here are two screenshots with some commands:

gui_movement_2_640.png
gui_movement_10_640.png

The above new wiring of commands to actions means also that I can now easily add new actions at any time, since it’s just a matter of implementing what the action is meant to do and otherwise adding it to the list, nothing more – all the wiring from Console and even from the rest of the GUI will simply adjust and include it too, as it should. In addition to this, there are a few more small but rather very useful improvements including clearer feedback from entered commands, resizing of the Console window so that it correctly resizes the two internal parts as well and a 1-line “history” so that one can recall the previous command at any time. In funny inherited weirdness that I’ll probably have to look at later on, it turns out that a resize has as side effect the reset of the font’s colour though! Such is the underlying nature of the inherited core widgets, this ever plentiful source of surprises of the most… surprising kind.

As you can perhaps notice in the illustration above, the GUI offers now the full list of actions defined in the communications protocol but also a few additional “actions” of its own, such as “pos” or “examine”. The reason for those additional actions is that they are simply quite convenient for the user and entirely a clientside concern: one might want to find out their exact position for instance and the GUI can expose that easily so I’d very much rather it did. There is of course no limit to what can be added there either but I didn’t spend time to polish this: the point for now was to have the very basics in and otherwise have it there as an example rather than anything else. For illustration, here’s the result of “examine” applied to the character itself:

gui_movement_3_640.png

While the Console remains to my eye the easier interaction option the user can have, I’ve nevertheless put in the work required to have a working mouse-powered option as well. So the skin.zip file got bigger and includes now at least one icon for each type of (inter)action known to the client. (And don’t ask me why is the “repair” icon showing a hammer crossed over by a rolling pin, I can’t begin to imagine where that combination comes from!) Those icons are then used in a new Context window that can be accessed with a right click on any item 1 and provides links to all the known actions. And since my GUI is not made with love 2 but with calculations whenever possible, this Context window simply uses basic trigonometry to just figure out where each icon goes so that all actions are shown neatly in a circle. As a consequence, if a new action is added, the Context window will simply adapt and include that too, there’s no need to add it manually and fiddle with its position through xml until the cows come home. Such are the results of bringing the ancient magic of the sine and cosine to the innocent lands of GUI and other computer ruralia! Lo and behold the euloran outer wheel of perfect roundness and plentiful context interaction (see the console in there for feedback on clicked actions):

gui_movement_4_640.png
gui_movement_5_640.png

Getting back to the actions itself, with the exception of lock and exchange, all the other protocol-defined actions are currently fully wired into the client, meaning that the relevant messages are created and sent to the server. The movement action is at the moment rather annoying and choppy so there is certainly more work needed there but I think it should still wait as some of the issues are likely to get smoothed out anyway as everything else advances (part of the choppiness is likely to be more a matter of the values I have currently set for the sync intervals for the whole world, for instance). Although I didn’t fully go into all the remaining movement part at this time, I start thinking that I’ll have to do that too at some point, most probably to finally kill that stupid of “leg length” and “falling below 0”, if nothing else. Nevertheless, it can still wait a bit, perhaps.

Arguably the most time-consuming “action” in all the above was the move/pickup since it required the whole inventory GUI with slots and equipped vs non-equipped and stacks and splitting stacks and moving items in inventory too and everything under the sun in there. There was a bit of mini-puzzle fun with the formula for inventory (non-equipped) slots, some annoying further misbehaving of that listbox that has the handy scrollbars and all sorts of weird discoveries regarding the number-prompt window. Nevertheless, the whole thing is now working, the Contents window got basically an upgrade, can move items about and is quite ready to be of further use as half of an exchange of any sort. The “move” action on items in the world is effectively a “pickup” – it will attempt to move the items to the next empty slot (calculated on the fly, too) in inventory and won’t care a bit whether that is possible or not possible. After all, you never know unless you try so my GUI here is not at all in the business of telling the user what they can and can’t do: it provides the widest set of options to everything and then dutifully executes what the user asked for. The inventory window updates with server-given data only on reopen (all it takes is to press i for that) but otherwise it will happily show the user whatever they thought they could do, pretty much. I’m quite sure that there are all sorts of details to change here on future iterations but for this first pass, I think it does as much as can be done for now:

gui_movement_6_640.png
gui_movement_7_640.png
gui_movement_9_640.png
gui_movement_8_640.png

For the number input window, I initially wanted to reuse the original window as it was since it had in place some input checks and limits and all those things that are in principle quite useful. The fun part started though when it turned out that it had of course all sorts of silly expectations – that got therefore cut away – and otherwise it used a horizontal scrollbar to allow the setting of the amount via mouse movement. This horizontal scrollbar was simply not able to use 64-bits integers without requiring otherwise a full update throughout the whole client, basically in all places where a scrollbar is used at all. So rather than indulge in such waste of time, I just dropped the silly scrollbar-set from this window and otherwise updated the remaining small part to deal with 64-bits integers, what else. Since I was at it, I added also overflow guards, as there were none but other than thatm the thing is (at least now) small enough to be perhaps of some use currently.

On a side note related to inventory and slots in general, I added again the quantity and quality labels to each slot but the trouble is that the maximum 64-bits value has 20 characters and to fit as many as that onto a slot, the font ends up so tiny as to be unreadable. So for the time being I let them be as you can see in the screenshots above, using a reasonably small font that means however that the full value might simply not be visible at times. I suppose this might remain as one of those low-hanging fruit to pick for any client developer out there.

Regarding the actions themselves, on serverside, at the moment, those action messages that the client sends are received, unpacked, recognised as what they are and otherwise promptly discarded. There’s quite a lot to implement on serverside before they can fully be acted upon as intended. Nevertheless, for current testing and illustration purposes, the serverside simply has some mock data that it sends to the client as “the world” and that is quite enough for the moment (though it won’t be for much longer, by the looks of it all).

With all the above in place and working, there would still be the exchange and lock actions and corresponding GUI elements as such. Other than that, there are various non-GUI parts that could be done (and need done at some point anyway) on the client, such as the management of Serpent keys and actually dealing with a change of connection/server on the fly.

Let me know in any case if there’s anything else/different you want done for the GUI part at this stage and otherwise if there’s something else specifically burning/best to tackle next. I should also mention perhaps that in the interest of documentation and seriously applying myself to learning the craft of GUIs, I have indeed reinstalled Diablo as well and gave it a spin – the windows are a pleasure, the tick-tock of idiotic walking is more annoying than any skeletons that might appear from the barrels. Nevertheless and purely for work interests, I shall therefore endure Diablo re-plays whenever required!

  1. This ran at first into a weird “attachment” thing that apparently was required in order to have a direct link between CS meshes and the game objects that the remaining client core works with more directly. And when that in place, there was the repeated and infuriating further run into the PS notion of “any item”, such as it still remained in the various parts that are still in use in any capacity: “Oh, by any you surely *don’t* mean meshes AND moving items, do you?” and “Even if moving items too, then *surely not* your own character, too!!” and so on and so forth, it’s like the devil is not in the details but rather the devil is a detail inside a detail inside a detail inside… []
  2. By now this made-with-love advertisement stands as far as I can tell for “it will blow up in your face but you should be happy about the blow up, because it didn’t mean to do you any harm!!”. Here’s an illustration, too, some typical result of the made with love approach.[]

August 20, 2020

The GUI, the Core and the Data

Filed under: Coding,Eulora — Diana Coman @ 3:49 pm

This report brings to you the latest news from the lively Euloran development grounds: the GUI is shedding code and growing in usefulness, the Core adds to its services the packing and unpacking of all user actions known so far, the Data cache refuses to be led astray into unnecessary sync swamps and gets its responsibilities right, while overall all three parts arrive at a clearer way of working well together and working well apart, providing what’s needed for others but minding otherwise their own business at all times. This last part of “minding their own business” took a bit of thinking over those past few days, as the newly added user actions didn’t yet have a clear place of their own and it’s precisely the clear and fitting definition of “own business” for each component that makes or breaks the whole: do it right and everything fits afterwards quite “efortlessly” or do it poorly and then you’ll fight at every step to fit all sorts of square pegs into round holes (and rebuild the swamps, too, as a result of that sort of mindless fight).

To describe the whole thing, it’s clearest (and not too long) to start from the beginning: Eulora’s client has currently implemented 2 main components, namely the Core and the GUI. The responsibilities of those components have been defined initially at a high level only, as the details were not yet clear. That initial definition got gradually more detailed as I implemented more and more of the actual communication protocol but broadly speaking, the original scope is still valid: the Core is responsible for handling *all* aspects of communication with server, from network data packing and encryption to actual logic for data acquisition and storage; the GUI (as stated in its name itself) is responsible for data presentation to the user and handling all user interaction.

While the above high level view is clear enough as it is, the trouble with going into the more detailed, concrete user interaction is that its very purpose is precisely to *change* the data so carefully acquired by the Core and so neatly presented by the GUI. Add to it that any user actions in the game are more accurately defined as *attempts* to change the data, with no guaranteed outcome until and unless the server actually changes the tiniest bit of the game-world in response to them and it would seem that those user actions are indeed straddling most uncomfortably the responsibilities of both Core and GUI: get the action from the user (GUI), send the request to the server (Core) but also change the data already so that the user promptly sees the effect of their action (Core *and* GUI?). At which point, the reader familiar in the slightest with the PS coding swamps might recognise that old pattern of “all GUI elements require a connection to the server and direct knowledge of message types.” Because indeed, it can easily *seem* like a …solution of sorts, except it’s of course no solution at all but simply the path of least resistance and even less reflection: it “seems” and therefore it is, why bother with anything else at all. Well, to keep this short, I’ll note only that I’ve been toiling for quite a few years precisely cleaning up the results of others’ even *more years* of following that path of least resistance.

The change to data as result of a user action is further problematic in that any change done by the client is essentially a guess: at best it gets confirmed in time by the server and therefore all is fine but at worst it gets undone (while *other* things may change too!) and that brings with it the potential further invalidation of all sorts, even of later user actions! Quite the thorny problem to handle, as any synchronizing problem ever is. So the naive approach to handling those thorns goes (again!) the route of yet another part of the PS swamps: we’ll effectively mark data in the cache as “dirty” or “clean” and then go back and forth and over it all to attempt sync with the server and to handle all possible cases that are not even all that clear in advance, since they can’t possibly be. For the most obvious trouble, there is no guarantee as to *which data* changes as a result of any given action so going this silly route of marking dirty data is at best a pretense to solve the problem but certainly no solving it at all. So no, naive doesn’t work any better than lazy above did but it serves at least to highlight more clearly the root of the whole trouble, namely that there are in fact and inescapably TWO data caches on the client: one is indeed the explicit data cache that is held by the Core but the other is the implicit data cache that the GUI keeps in the graphical engine itself! There is no way in which the GUI can show a persistent representation of data without having therefore a cache of that data and as a result, the GUI can’t shirk taking on the full responsibility for the *local* data cache where it can make whatever changes it wants, without however expecting to have any direct access to making changes on the authoritative data cache that the Core maintains.

In a nutshell, the Core aims to maintain its data cache as closely as possible to the server’s view of the world, without caring at all about anything else, while the GUI aims to maintain its data cache as a reasonable guess of the world essentially: as responsive as possible to user actions and as close as possible to what the Core provides. This means that there is really no need to mark some data as dirty and some as clean in either of the two data caches: the one in Core is always and by definition as clean as the client can ever get; the one in the GUI is at best a fresh copy of that in the core and at all other times a… guess. The Core will regularly refresh its full data view by asking the server for a World Bulletin (and then following up through the world hierarchy as relevant), while the GUI will regularly refresh its full data view by asking the Core for what is in view at any given time. Whenever a user action happens, the GUI will promptly provide to the user whatever feedback it can and otherwise it will simply ask the Core to communicate that action to the server. There’s no need for anything further really: whatever the result of that communication, the changes (if any!) or the undo of attempted changes (if any!) or in other words whatever effect the action has or doesn’t have will be obtained anyway as part of the regular refresh of the data done by both the Core and the GUI on their respective caches.

While the above general principle works for all user actions, there are indeed some that are easier to handle than others. In particular, movement is quite apart from the rest, as it’s basically a “continuous” sort of action that moreover needs to have an immediate effect shown to the user. So the GUI will have to balance here the need to synchronize the player’s position with the server to avoid annoying abrupt relocations and the need to keep the user’s costs low as all messages sent to the server have a cost and nobody will be all that happy to use a client that racks up huge costs just for moving about the land. Nevertheless, the great part in all of this is that it’s all clearly and entirely the responsibility of the GUI and so it can be all contained in one single place and none of Core’s concern. The less than great part in all of this is that the actual movement part in the current GUI is still afflicted to some degree by planeshittism 1 and so it will still take some work to figure out a way to make this work, preferably cutting out or at least avoiding that remaining bit of swamp.

Other than the above specific trouble with the movement, there is still some work to be done to implement in detail everything needed for all the other, more straightforward actions. For now and mainly as a basic test, I’ve implemented on the client side just the simplest of them all, the “explore” so that I could confirm indeed that the whole approach works: the GUI reacts promptly to the user command and asks the Core to send the request to the server, the Core packs it correctly and sends it, the server correctly unpacks it and recognizes it as such. For that matter, I’ve implemented already the packing and unpacking (+tests for them) for all the actions, so the next steps will focus specifically on the higher level part of this and first in line, the clientside required bits and parts for performing those actions.

  1. Without even counting all the parts that have been shed already in this area, there’s still a psCharControl and otherwise a psLinearmove that does the actual calculations and heavy lifting but also a psMovementManager that can anyway “manage” only ONE actor at any given time and through a tangled web of back and forth calls from actor to manager and the other way around as well as from other parts to both actor and manager or to actor through the manager as well as to the manager through the actor – the usual PS “style”, really[]

August 12, 2020

What the Fuck, Planeshift?

Filed under: Coding,Eulora — Diana Coman @ 5:21 pm

There’s no other title fitting this. Here’s the priceless context for the question above, context worth not one thousand but 100 000 lines of open source code, at the very least:

gui_inv_2_640.png

The above is the amazing result of trying to make use of all those psSlot and psSlotManager and pawsManager with their drag-and-drop wonder and marvel functionality, a true crowning achievement and glory of open source development. I ask the bloody “manager” class to drag something across the screen, as it claims it does and as a result it… breaks everything else pretty much, as nothing really updates anymore and the thing leaves a trail wherever it goes. But all is well, I’m sure, at least according to the manager and to everyone else working very hard and tirelessly and wonderfully and so on 1.

As I am apparently still naive enough to try and debug even the above sort of madness, I ended up in short order following a back and forth web of calls that are increasingly bringing to mind the way chaotic systems tread their path, ever random when it concerns the very next step, ever building nevertheless the same known shape in the end, as predictable as it can be. To give a clear view of how this goes, here it is: the widget calls the manager and the manager calls then the mouse class that calls back the widget that calls its parent class that calls back the manager that calls -at another time- the mouse class that calls…

Fortunately, those days I get at least faster out of such naivety and optimism and so I stopped just about there and didn’t waste any further time with it but looked instead at some way to do it more directly. I suppose by now it’s not surprising that all that crazy web of shit for “drag and drop” was anyway on top of and to the side of a somewhat less crazy “move with mouse” and then it turned out that even that was spurious and in fact the core class of ALL widgets in that bloody gui even had a method specifically for …”center on mouse” sort of thing – and it even worked! So I made therefore my own “drag” as I had made my own prototype inventory window and my own prototype console overlay and my own seedling for the rest and let the many managers rot and fester in there unused and increasingly cut away from everything until that day when I’ll finally get around to delete the whole mess too and there won’t be any difference other than having suddenly a much smaller client that is however way more useful. At any rate, as a result of this most recent adventure, I stand corrected in my initial opinion that there might still be at least a tiny amount of utility in all that PS code – there isn’t, there never was, there never can be.

As to the current status of those windows and overlays on my list: I have now a perfectly working way to drag stuff about (not yet to drop it but I don’t expect that to be a huge problem, seeing how I am not going to touch any of the PS shit for it, not anymore), as well as working prototypes for both “Console” and “Contents” windows, with a few commands wired in already mainly for testing and poking that Contents view. Everything in those is created from code and without any xml in the loop so that the Contents view for instance can easily create as many slots as one wants, there is no limit of any sort anymore. The logical thing is probably to simply create slots *as needed* and without limit but for now I disentangled (and cut and wrote anew) enough of that underlying widget that has scrollbars – the scrollbars are useful but the design of it otherwise required a bigger hammer to bend into something usable in the least. As you might notice, I even gave in and added arrows for the scrollbars! In any case, all the images and graphics and colours are just for testing for now and I’ll gladly use better ones /let someone else design any window whatsoever. For now, here’s how the prototypes look like (in order: a test with adding and replacing stuff in the contents view, based on id of the *underlying* object instead of id of slot and other madness; the successful dragging of ‘shrooms with icon and everything else):

gui_inv_1_640.png
gui_inv_3_640.png

For the next steps, there would still be first a bit of cleanup around those latest parts and assorted cuts made for it. Then I’ll have to see if it makes perhaps more sense to get back one round to adding more working parts on the data side + communications and then let that drive any further development of the GUI itself. At least I’m now quite satisfied that I’ll be able indeed, one way or another, to add to the GUI what may be required, even if some parts are very likely to throw some more wtf of the sort illustrated in the very introduction to this and eat as a result rather more time than I’d like to spend on them.

  1. It could never not be great and wonderful anymore either, there is no such thing as something *not* being well, nowadays it’s either great or otherwise forgotten or simply not mentioned for it’s surely the mentioning that is causing the problem in the first place, obviously.[]

August 5, 2020

You Will Have to Push My Buttons

Filed under: Coding,Eulora — Diana Coman @ 3:02 pm

Until such time that you make any buttons of your own at least, it’s now all settled, clear and without a choice: on all Euloran soil, it will really be my buttons you’ll have to push 1 and my views you’ll have to use and my winPAWS 2 that you’ll look at. For there was/is barely anything useful in all those 100 windows that were each terribly important on their own, each with their own layout xml file and own hand-rolled loader and own parser and own further numerous attendant misfortunes that are inevitably linked back to that mighty struggle of trying to have a singleton manager without admitting however of having any *specific* manager. It’s a riot reading the code and the famous “software patterns” and even all the Object Oriented paradigm in this obvious key. It’s even funnier now as the end of the swamps is otherwise in sight and as the intelligent idiots are out of my way and staying there in their narrowly delimited circle where they are taking over the world, I’m certain, only it’s each of them taking over their own world but that tiny detail is entirely unimportant and can’t possibly matter, can it? They are after all solving problems with code and moreover solving “problems” picked and ranked based first and foremost on what they are familiar with and the sort of solving that they have done before since that has to be the best way – the more independent, upright, sane and intelligent way. Not that this has anything to do with it being also the only way that is still in the least open to them, no, absolutely not.

Back to that fast growing life of Eulora, the dive into the full depths of PAWS was essentially successful if not lacking in crashing, segfaulting and downright maddening experiences: the whole thing is not unlike a carefully constructed castle of sticks that stands for as long as it’s untouched and then unravels entirely at the merest movement of air around it. There is in fact already a set of “basic” elements and those 100+ windows are on top of that but even those “basic” ones are at times anything but basic. The most screaming out loud example of this would be the humble title of a window/widget/whathaveyou – in order to show *any* title (as in text, a few letters, nothing more), the “widgets” require a border and that requires buttons and styles among other things and those in turn require preferences and all sorts that further require the requirements of requiring required requirings… Why does the title part require buttons or borders or all that? I suspect it’s because someone was solving there with his code a lot of problems that I don’t have! So I took instead the smallest thing that I could find, trimmed it further and added it directly to the core “pawsWidget” class so that any damned thing CAN now show some text as title without requiring anything more than the… text itself! Shock and horror, I know, just look how violent I am with titles and buttons over here!

Moving paws on the path further, I started to simply write the code for some generic “views” – the sort of windows that can be used for a purpose and otherwise displaying whatever information is relevant. At first, this ran fully into all sorts of intricate weird: the show that won’t show, the click that won’t click and other such crazy stuff from working with the castle of sticks where everything interacts with everything else and that results in a whole ritual -where order of steps is important, too!- necessary for achieving the tiniest effect. Disentangling all that was no pleasure but it’s still the fastest way I know of to get to the point where something useful can be done with the whole mess and it worked with this mess as it has always worked with any and all messes I ever encountered. As a result, I am quite confident now that I *can* make my views, windows and overlays in a reasonable amount of time and *without* a whole lot of clutter sticking to them.

For the initial practice, I first cut to pieces that “bar” at the top and then put back together only a skeleton of it, as an overlay that has so far only the quit and help buttons – and those are my buttons indeed, made through my code and in a more direct way than the original entangled messes. The reason why the whole bar itself is still there as a thing at all is that it holds also quite a bit of the keyboard interaction part so there’s more that needs to come away before it becomes fully clear if it comes off entirely or not. At any rate, otherwise I changed that pawsManager to actually act as one and therefore I have now in place the building blocks to allow the desired approach of “one window at a time and otherwise overlays”. This also cut a lot of crap dances around all sorts but mainly in the sense of not using a lot of code, not yet in the final stages of deleting and discarding it too. For illustration, here’s the new “menu bar” overlay and the confirmation window that a click on the Quit button made appear:

gui2_4_640.png

Once I had those buttons showing and working as I wanted them, the next step was to add various bits and pieces that arguably come in handy and could at least build on what I had so far. So I enhanced that minimal “helpwindow” of last time, as it’s in my view the prototype for a generic view aimed to show structured text 3: it has now a title and there are also functioning scrollbars that use Eulora’s own icon as tracker – I rather like them; the +/- signs for opening/collapsing a topic get shown now with their own icons in the skin.zip file; there’s also further experimenting making sure that both background and text colour can indeed be set as desired:

gui2_1_640.png

I’ll have to note here that the above is still not giving a damn about stuff like “borders” and stuffing buttons everywhere, from title to sidebars and anywhere in between. To be honest, I personally prefer by far the transparent and minimalistic windows and I make a half-hearted exception only for text really (as it’s rather easier to read when it’s the traditional black on white or gray on black). So if you’d really, really want those windows to have anything further, please say it sooner rather than later, as left on my own I doubt I’ll find much reason to add even “title bars” as separate things, let alone stuff like border-this and border-that or decoration-here and decoration-there.

For further practice with making whole windows really from scratch rather than starting with a previous overgrown thing and cutting thorns at all steps while still remaining with a crooked trunk, I made also an “Info” view that is meant to show an image + name + description for anything in the world: it’s barely a page of code and it even sets its size depending on how big your screen/eulora is, aiming only to maintain the ratio of its minimal size. The image gets indeed re-sized to fit exactly the space available in its side of the whole view so that’s perhaps not ideal but it can be changed for sure. To fill it with something for testing purpose, here’s the very info on Eulora itself, first in the transparent version and then with the black background for that half-hearted concession to showing text on a non-transparent background:

gui2_2_640.png
gui2_3_640.png

With the above done, my current list of generic views (aka windows+overlays) reads like this:

  1. Command line – TO DO. This is in my opinion crucial as it’s meant to be the game’s console basically, the only place for the player to enter commands and thus be able to do anything, whether there is a button for it yet or not. This would be an overlay, I should think.
  2. Confirmation – prototype DONE. This is a very basic yes/no type of window, aka one question + 2 buttons, modal thing. The current prototype is perfectly and fully working, if entirely transparent. The wiring of the buttons is now mine too, entirely circumventing the several layers of passing the buck that used to be “the way this is to be done”. I don’t know if there really is a need for any sort of other “view” as such – if it’s just a text message, it can still be shown either directly on the screen (that fading sort of thing, dropping down as it faded) or otherwise in the console/command line thing above, I guess.
  3. Waiting – prototype DONE. This is meant as a placeholder/information for when the game is not available/unable to display the world as such. It simply takes over the whole game window and it displays an image + a label/text at the bottom. The prototype is fully functional and even wired in its correct place aka displaying at the start while the client is waiting to obtain from the server the most basic stuff such as who am I, where am I and what does this world even look like. I kept that boceto drawing as I quite like it (even more so than the “splash” thing).
  4. Info – prototype DONE. This is meant to show image+name+description (or any subset of those) of anything in the world.
  5. Contents – TO DO. This would show everything that is “inside” any given object in the world. As such, it could show the contents of some container but equally well the player’s inventory or the equipped items or a storage or the contents of an exchange of any sort. In my current hazy view of it, this will simply show as many “slots” as required, each with their own icon + text (e.g. stack count and quality or similar) + tooltip (to show the name of whatever is in there).
  6. Exchange – TO DO. This would be made out of 2 Contents essentially, with wiring as appropriate for whatever specific exchange is shown at any given time. Basically the “inputs” would be the 2 items whose contents are to be shown on each side of this view. In my opinion and just as an example, this can even work to provide a GUI for activities such as “equip” since it’s an exchange – stuff from inventory being moved into equipped and the other way around.
  7. Management/Ownership – TO DO. The details of this are not very clear to me currently but as I gather that there will be various types of managing involved (e.g. jobs or land), I’ve added this to the list as a generic window that can hopefully be made to cover adequately the management of whatever is needed. I’d say there is still time to figure out this in more detail and there’s no rush – it’s on this list as a type, not much more so far.
  8. Skills/Self – TO DO. This is quite hazy too but what I mean here is mainly a view that is able to show also things that require something other than image or text: perhaps bars/percentages or the like. I am not fully clear on this part yet but there’s certainly time for it to clarify.

As next steps, I would push this a bit further still to fully transform that “helpwindow” into its intended generic structured content view and make otherwise at least a prototype of the Console/Command Line and Contents generic views. The Contents one is quite important because it means figuring out all that slots business and dragging stuff from one place to another, which is another potentially big chunk of this. Hopefully as part and parcel of that, I should be able also to finally delete another pile of useless code, as it ends up entirely stranded. In terms of specific GUI bits and icons, the more I work with this, the fewer of them I want in there so do let me know if there are *any* parts that you absolutely want in there as otherwise I’m afraid I might easily end up ditching just about *all* of them and have the whole “skin” consist in something like a plus, a minus, a quit icon, a help icon and a tracker for scrollbars, not much else really. Of course, anyone can otherwise ship to the client any .zip file with anything at all but the default client will not use anything else and keep those windows as minimal and as invisible as possible, by the looks of it.

Other than the above, let me know also if the approach of “generic views” makes sense to you and whether you’d like any others added to that list, even if just as general types, to be perhaps fleshed out (or discarded) later, as everything gets into more concrete shape.

  1. Heh. I know.[]
  2. Well, they’d be windows except when they are overlays and otherwise they are made still on top of and with various bits of the paws that apparently would really, really want to be of some use, if only it hadn’t been so thorougly misdesigned and abused by means of pointerfuck…[]
  3. By structured text I mean here text that has a hierarchy of parts that can be shown/viewed separately and allow navigation through a tree of contents.[]

July 30, 2020

What’s Eulora’s GUI Going to Be Like?

Filed under: Coding,Eulora — Diana Coman @ 12:57 pm

Against my own expectations, it seems there’s little resistance left even in that mess of paws: it took less than a week to map it all out quite fully this time, discard yet another set of dead parts of it, cut its remaining tendrils and pretense to “independent plugin, part of a common trunk, not just some code in the client”, bring it therefore quite in line (though not to internal sanity, it still is what it is) and ready to be made use of, without crashing and without complaining that it doesn’t have graphics or skins or resources or whatever else it previously imagined that the user must provide. For instance, here’s the gui just using the system’s mouse pointer if nothing else is available and the help window gladly showing whenever it’s asked to show, looking as best it can with whatever is available at that time:

pawsgui_1_640.png

Having thus made sure that indeed, I can make paws do something useful after all, I’m literally staring at the pile of neatly sorted entrails of the whole thing and I can’t make up my mind for the life of me – *what* is even the desired use of this paws-paste? So I’ll first write down the brief description of what’s there and then I’ll spell out this generic question into several more specific questions that can hopefully be answered.

The current client’s GUI interface is essentially a lame and broken attempt at implementing the world hierarchy from a… visual perspective 1. There’s a top “controlling” 2 window that keeps track of all the others and does in fact the generic figuring out as to what window is the user interacting with, which ones should show and which ones should hide, all that sort of thing. Being of course a visually controlled world, the top window ends up being… that menu bar at the top (see? it’s at the top, right?) of the screen, with the inventory and the quit button and whatnot. It’s true that there is otherwise *also* a “mainwidget” thing but… that’s just the overall container and doesn’t do much at all. Don’t ask me why can’t that mainwidget be actually main and therefore control the rest, it just isn’t. And because those are just windows and the whole codebase at the time simply couldn’t do without managers, there’s on top of this top a pawsManager with its very own textureManager.

The pawsManager is meant as far as I can tell as the entry point to the paws itself – at least when entertaining the delusion that paws is or can somehow be separate of the rest. The textureManager is one of those endless glorified list-keepers: it keeps its own list of filename-resourcename equivalencies for all images that the GUI uses and otherwise interfaces load/find from the engine’s own textureManager, of course. The meagre claim to purpose it has beyond that of list-keeping would be that it packs in one place some format-related processing of 2D images that are supposedly helpful in some cases (though it’s unclear if there are all that many such cases in current use). Anyways, aiming to keep changes to a minimum, it can live as such and keep its list too, since I have no intention of saddling EuCore with any internals of the GUI/skin now. There’s of course a whole dance around in both pawsManager and its textureManager with translating strings (literally, the “localization” because apparently it makes somehow sense to shove that concern in there), defining “styles” (there’s skins and then there’s… styles; because one S is not enough and nevermind there’s no game worth even one capital letter, yet, we’ll add styles to the interface and that’ll be grand!) and making oh-so-sure that one is never, ever, under any possible circumstances somehow left without – gasp! – a loaded “skin”. Additional, even smaller concerns abound too but by now we get to the microscopic so I’ll let them be already, suffice to say that if I’ll ever need an example of pointless polishing of irrelevancy, I can certainly use that code.

Past the 2 managers and 2 top-widgets, there is the indistinct mass of …more widgets. Basically *everything* in the GUI is a widget, from one single slot in the inventory window to the whole inventory window itself. Here I can happily report that indeed, the “off with its dolly” task was most satisfyingly carried out: there were 2 (two!) classes on top of all the usual widget+xml defined just to *support* that and several tendrils sneaking out into non-paws parts too and oh, what joy to cut them out already!

Dolly cast aside and readily forgotten, the reduced pile of widgets is still a considerable pile and otherwise waiting for either full chop or partial cut, depending on what exactly turns out in the least useful in there. The “design” of the whole thing is that each widget has a code class (in client/gui mostly) that is meant to implement what the widget does (and even if it doesn’t – there still has to be this code present as a means to register and create the thing, useless as it might be) and an .xml file (in data/gui currently) that defines in principle the layout of the corresponding window to be shown in game, including size, position, contained widgets and used “resources” aka icons and other images. In practical terms this means that new or different widgets still require touching the client, there’s no “download this” and run with it really. At a stretch, the .xml layout files could be made perhaps part of the “GUI” .zip and so something that can be downloaded from the server and used but there’s a very tight coupling between the code and the .xml so that it’s unclear to me if there’s much point to it anyway. So Q1 of those more specific questions would be here: is the layout of various GUI windows something we even consider part of a “skin” definition and hence up to GUI designers to produce as part and parcel of offering a client skin for use?

Even after the above trim, there still are about 100 widgets with their corresponding 100 .xml files, so I would rather not list them in here – the list is anyway quite easy to get with a simple ls data/gui in the deployed client’s dir. The more specific question here is Q2: what windows do we actually need in the whole interface anyway? I can’t quite see the point of 100 windows and it really all seems to me more a matter of ill-thought “design” in the first place, fitting slots in places by hand and adjusting them to be just-so, the usual sort of “artistic” approach to it all. I rather think there has to be a small subset of elements that will do it all and better anyway but at least currently I don’t even quite know where to start from with it, as it’s not all that clear to me what sort of things we need in the first place. I can even see the point in saying it’s too early to bother with that and move on to implementing more of the functionality so that what is needed emerges from there but on the other hand the GUI has to start with something/from somewhere too.

Going through the list of main things as seen from a player perspective so far in Eulora, the Q2 above can get further refined: the most complex window in there is the chat window – does this even still make any sense or should this be only and at most a cli window in fact? Windows like the storage for instance were relatively ok in the beginning and then an utter pain when one has a full storage, reminding me quite precisely of the more generic windows-vs-cli computer use really – do we still want a storage window at all? And where does this stop anyway, as on one hand I can keep going and wonder if the inventory window is useful as such (perhaps it’s at least pretty to see them all!) or the container or …which of those 100 total, in the end? (Then again, I’m a cli radical apparently (hence my asking here), regardless of all the fun I had making textures, heh.)

Windows and widgets aside for a moment, the GUI graphical elements as such would be a set of tiny icons like arrow-this-way and that-way, backgrounds, buttons, scrollbars and the like. Currently even this is a bit of a mess in that there’s no standard set at all, but a proliferation of arrow-up and arrowUp that are two different arrows pointing “up” (and this is probably what they tried to solve with the whole “styles” layer of grime on top, myeah). Nevertheless and as boring as it sounds to do it, a basic set can certainly be extracted and then forced even upon the existing widgets, certainly. The potentially troublesome part though comes from a current ugly mix of actual GUI elements (e.g. buttons) and 2D representations of game objects (e.g. a key in inventory). The currently deployed client makes no real difference and effectively lists those 2D representations as GUI “resources” like all the rest (so for all the fun, one could in principle very well set their buttons to look like Omlette Du Disgorge, why not). While I have nothing against buttons looking like omlette-du-disgorge, game objects are a very different category from GUI elements and I don’t think they should be mixed at all. Even focusing solely on the very practical perspective, GUI elements get their representations (if any) from a skin.zip file (if it exists at all), while game objects are first of all a potentially infinite set, not known upfront and even potentially changeable on an item by item basis during play. So question Q3 here would be: does this separation make sense or is there some better way to go about sorting this out? (As I’ll need to sort out this part at some point anyway, it can’t really remain as it is.)

All the above out of the way and in the open for discussion, I can happily report that the client’s code continues to shrink for now as it increases in usefulness and otherwise there are still parts that will end up most likely discarded in the end, as they get increasingly left behind (the original net part comes first to mind). While there is still quite a lot to implement on both client and serverside, I think the next steps will have to be alternating client and server development, introducing the new things (finally!) and sorting out otherwise what troubles remain, as they pop up. In other words, there’s still a lot of hard work ahead but finally getting out of the swamps and it’s such joy to be out of them too!

  1. The attempt is so visible and the whole thing is so broken from so many points of view that I don’t even have the heart to laugh at it anymore. It’s one of those things that are not even wrong yet.[]
  2. The corresponding parent classes are even called exactly that, for bonus points of similarity of names being a great thing in code: controlwindow and controlledwindow.[]

July 24, 2020

Those Zip Files of Eulora’s Client Graphics

Filed under: Coding,Eulora — Diana Coman @ 12:39 pm

Wandering all alone and growing quite bored of seeing the same familiar empty space, the twisted hopeful glanced all of a sudden an unexpected shiny item sticking out of the ground and in plain view:

csmesh_1_640.png

What could that be and where did it come from? It wasn’t there before and there’s no guarantee it will remain there either but there is now finally the possibility for any number of such items to be anywhere at all in Eulora. And now that this part is done as well, the client has finally all it needs to render -though not yet to control or interact with- all three main types of 3D things: the world itself (terrain, sky, water), moving characters be they NPCs or players (Cal3D) and non-moving items (CS meshes). In all cases, the “graphical representation” is simply a .zip file. The contents of this .zip file can change and there’s plenty of flexibility in there with only a few “must-have” things nailed down for now – even those can change later on without much trouble, since a .zip file remains a .zip file otherwise and knowing how to use what is inside is entirely a client concern.

The current client 2.0 aims to look for and use what it knows, while not getting stuck on anything additional that might be in there. So it uses currently some very simply conventions regarding filenames inside those .zip archives and simply picks up the first matching files it finds inside: anything starting with “tex” is assumed to be a texture and its extension is assumed to match the format (.dds, .png, .jpg all work fine); anything starting with “mesh” is assumed to be a mesh description and interpreted depending on the context as either CS mesh format or Cal3D format (binary or xml depending on extension); anything starting with “skel” is a skeleton file; animation files are always exactly walk.caf, idle.caf (so far those 2 are the only ones the client knows about). As examples, here’s what the current test set of .zip files contain:

  • For Outdoors (the filenames here are fixed indeed as they all have very specific roles):
    • basemap.png
    • heightmap.png
    • lightmap.png
    • materialmap.png
    • norm_1.dds
    • norm_2.dds
    • norm_3.dds
    • normalmap.png
    • sky.png
    • tex_1.dds
    • tex_2.dds
    • tex_3.dds
  • For Cal3D:
    • skel.xsf – the file containing the skeleton in Cal3D format. This can be either xml (.xsf) or binary format (.csf) and the client will simply use whatever is provided.
    • walk.caf – the animation to be used when this character “walks”. This has to be currently in binary Cal3D format because reasons.
    • idle.caf – the animation to be used when this character is standing/sitting still. As for walk, this has to be in binary Cal3D format.
    • mesh_*.xmf – this hopeful happens to be made out of 26 meshes so there are 26 such files, simply numbered mesh_1.xmf to mesh_26.xmf. The client will just load as many mesh files as it finds, that’s all.
    • tex.png – the texture to use on all meshes. In principle there could be as many texture files as meshes so that each mesh has its own texture. It’s not a big trouble to adapt the client to use them but at the moment it just picks the first tex* file it finds and it uses it for all meshes in that .zip file.
  • For CS mesh:
    • meshfact_1.xml – the description of the mesh in CS “meshfact” xml format (NOT library format, see below the more detailed discussion why not library).
    • tex_1.png – the texture to use for this mesh. This can be in principle *any* graphical format. The current client can and will happily use .dds, .png or .jpg just as well.

Compared to the previous iteration, the “outdoors” type bundled together water, terrain and sky because in practical terms they always go together indeed. There is here one part that I’m still hesitating about: while the size of “sectors” is fixed and doesn’t need to be therefore sent (or at least not at this level), it’s unlikely that this will also match any indoor areas that are to be separate areas as such and the trouble is that the loader needs to specifically set those. One way to go about this would be perhaps to make a different type specifically for “indoors” and give all this additional information required there in the euloran object itself (arguably even the outdoors type might be better off with some specified there, if or when they may change). The main question I have here is: what is fixed about outdoors and indoors (as the cs part has a lot of parameters and very few turn out to be of any use in the end)? More specifically, given the protocol specification, the size of an outdoors sector is fixed and as a result, the client can just create a fixed-size sphere for “sky” for instance and a fixed “cell” size for the terrain and so on. Is this size fixed for indoor places too or could those be smaller/larger?

As to the latest addition from the list above, namely the CS mesh items, getting them to work included a few ugly surprises that slowed me down a bit more than I initially expected. Nevertheless, as there’s otherwise less resistance left in that paste than at the initial stages, it didn’t take all that much to whip it up into something useful. First, the format I had used previously based on existing client’s “art” files turned out to be less than ideal for a rather unexpected reason: the CS loader would happily load it but it wouldn’t provide any means to obtain directly a way to reference the loaded mesh – it assumed the caller would know that from somewhere else or otherwise find it somehow 1. To fully get this: CS insists on referencing everything by “name” hence effectively by string-ids and the names of loaded stuff from a library file are …specified in the xml but otherwise not returned by the loader itself! So to find a factory or a mesh, the code would need to either read it specifically from the xml (ugh!) or otherwise assume it matches something else through some convention forced thus by… lack of options (because no, I don’t want to have the server now be concerned with fixing the names of meshes clientside, wtf). The solution I found to this is to not use “libraries” at all since anyway each of them contains exactly one meshfactory and then load that factory with whatever name there might be in the xml but *change* it from the code to the name that is useful to the client (and that’s …the ID of the darned object, ofc, what else, only made into a string to fit CS’s expectations, yes). It took a while to figure this out, mainly because finding the way to simply rename things is made… interesting by means of Object Oriented (OO) obfuscation layers so that the code can end up at times like this: meshWrapper->GetFactory()->GetMeshObjectFactory()->GetMaterialWrapper()->QueryObject()->GetName(). Count the layers and laugh at it but know that it’s not even the worst that OO can do!

Once the above sorted, there was some more work to cut that GEM crap on items too and then to follow up and look into all the resulting, inevitable crashes given the spaghetti pointers running all around. At least I used the opportunity to look also into the way that PS used to do “effects” – it’s a useless pile of crap on top of some tidbits of usefulness from the underlying CS, not that it comes as any surprise. The surprising part was that “labels” (e.g. names of characters) used to be… “effects” and thus bringing in the whole crashing monster of stuff. Well, labels presumably will be useful so they are on my list to add as part of sorting out the 2D part of the interface but I refuse to bring in for this reason a whole set of useless “managers” and corresponding plugins and 1001 classes that do the same thing only now it’s called labels and then it’s called shadows and then again it gets its name changed so that a different “contributor” can sign at the top of the file. Such contributions that one is way, way better off without any of them.

At any rate, having now sorted non-moving items as well, the next step really has to be looking into the 2D GUI stuff more closely, hence… PAWS. There’s no estimate I can really make (only heavy guesstimates) on how long it will take to make *that* into something useful but I’ll keep to my heuristic that served me very well so far and I’ll write up whatever stage I got to, whenever it needs unloading and certainly not later than in 2 weeks’ time.

  1. How? well, that’s I suppose exactly how one ends up with all those .xml files just to tell what is in those other .xml files and then load some upfront and expect they know it all, too. Like will all madness, it’s not that there’s no logic involved, there is in fact plenty and at each step – it’s only that the root is entirely misplaced. And yes, I am fully aware that those perceiving the roots of my own work as misplaced will therefore assign the madness label to my own work and my own self. A label though never did anything though, nor will it ever do by itself.[]

July 16, 2020

Client-Paste Says Yes, It Can

Filed under: Coding,Eulora — Diana Coman @ 5:12 pm

There’s still quite some work to do before having a fully working client as well, but I can happily report that the most important part for that auto-updating version of the client is indeed in place and working: an initial prototype client 2.0 can now start happily with nothing at all (no keys, no graphics, no knowledge of the world at large) and then generate keys, obtain an account, retrieve (and check) sector and model files, load them when it got them, even let the resulting euloran move about and admire its reflection in the wonderful water by the rocks:

client20_1_640.jpg

For future reference and current unloading of a fresh set of troubles sorted, here’s the list of main updates done on the clientside:

  1. EuCore (data acquisition logic, Ada):
    1. Setting RNG source via config file: whatever string the user provides in there (e.g. “/dev/urandom” or “/dev/ttyUSB0” or “/silly/ass”) will be used as source of “random bits”; NB: the indicated source, whatever it might be, is considered already initialised in whatever manner is required, since it makes no sense at all to try and initialize from EuCore’s code a source that can be set to anything at all. For all the apparent “easy” label on this additional knob, there was a significant headache to make it right, since the RNG source is used from both C/CPP code and Ada code so that it has to be set across language-borders, not to mention in the precise direction previously-avoided-to-keep-things-neat-and-less-confusing. Not that I can recall *any* time when I got to keep over long term any of those parts that were easier, it’s just not my thing or something. What can I say – it’s done now and it’s working (the default is urandom).
    2. Setting a “sync” interval – meaning how often the user wants EuCore to attempt to resync its local data with the server. This is important mainly as a means for the user to find the compromise they want between generating traffic (which has a cost) and otherwise keeping their local data cache up to date (since out of date information can presumably make play rather less than ideal). Worth noting here that this “sync” interval is important for movement update and for requesting World Bulletins regardless of other triggers that might apply.
    3. Creating account and obtaining BOTH sets of Serpent keys from the server – this got a refactoring and update to reflect the clear separation between EuCore that does *all* data logic, acquisition, storage and so on (hence it’s the only part that can request creation of RSA keys for a new account for instance) and what is simply a dumb GUI – the former “client” that will focus exclusively on using and presenting data that is present, nothing more. There was also still pending – due to previous lack of clarity at least in my understanding of this part – the issue regarding the initial set of Serpent keys for communication with the client. While the protocol allows for the client to generate those and send them to the server, at the moment the client will simply keep sending request for an account until it receives those too. Perhaps at a later stage I’ll get back to this and implement also the option + knob to generate them locally and send them to the server but this can certainly still wait.
    4. Updated EuCache (the actual data storage structure that provides thread-safe access) to reflect the latest, streamlined data model.
    5. Updated the data acquisition logic to actually send requests to the server for each and any bit of missing data (be it object or file) as soon as it becomes aware it’s missing. This includes proper packing of file requests in as few messages as needed (instead of firing up the wasteful one message for each request).
    6. Updated handling of objects to fully update local data cache correctly (in short: adding those in server’s view and not in client’s view, deleting those in client’s view but not in server’s view, asking for anything missing and fully walking the structure at regular intervals, to ensure re-sync).
    7. Updated handling of file received messages so that chunks are saved as received and the Keccak hash of the file is calculated + checked against expected value at the end, deciding on whether the resulting file is kept and made available to the GUI or otherwise deleted (+ new request sent to the server, to resend it). NB: the approach here is very basic for now in that chunks are literally saved in the order received and therefore it’s enough to have 1 message lost or out of order to result in an incorrect file and therefore repeat the request. I’m sure that this can be made better – not so sure though that it’s worth the trouble to sink time to make it better *right now* and so there it is, low hanging fruit for anyone who wants to improve their client.
    8. Updated interface towards the GUI to reflect the new understanding that the actual needs of the GUI are way, way simpler than the full complexity that the protocol and more generally the world structure allows (and here’s an example of where the doing-it-all kind of made me stumble because even when it’s the “same thing”, it’s from different perspectives, of course and so I need to keep them all in mind at the same time and change swiftly between them, oh joy). At any rate, now the interface provides the sort of packing that the GUI finds useful, while taking otherwise advantage of EuCache’s data structures that reflect the actual hierarchy of the world. That’s pretty much the part that wasn’t quite that obvious for me until now – the dumb GUI is in fact way, way flatter than the actual world as it were and as such, there’s really no point to make the GUI more intelligent than it can handle. Let the GUI be an idiot that is forever concerned only with “what can I see *right now*” and that’s that, everything all of a sudden starts to fit, too.
  2. GUI (the former “client”, cpp) side:

    1. Refactored the SMGData interface to work with the latest EuCore interface and provide a single point of access to all data, from objects to files. Sorted out here some unexpected issues with importing constants from Ada into C as well.
    2. Nailed down a working model for this whole GUI stuff: there’s a logic (such as it is) part that will then make use of all the paste that used to be pompously “the client”, from pscelclient to psmovementmanager and whatever. Sorting this out was a pain but the result is a very welcome insulation from the rapidly-spiralling-into-madness mess. In other words: while the mess is still there for it will take ages to fully eradicate, one doesn’t have to work neck-high into it *at all times* anymore! At times, a descent into that is still needed but I’m finally at the stage where it’s “at times” as opposed to “all the time”.
    3. Updated and refactored loader actually capable of making full use of .zip files that were acquired *after* client startup! This was such a pain to get working, as the virtual file system thing is rather poorly documented and quite clunky at times. Nevertheless, it … works!
    4. Prototype implementation of “soft reset” – essentially the PS code insisted that one needs to go through 1001 things in 10001 places to just clean up if/when the sector changes for instance but then it turned out that CS has this nice and shiny red button to …DeleteAll! And once that is done, PS can go and get stuffed since there’s not much it can do anyway. So the prototype so far aims to simply do such a soft reset if something crucial like the sector or the player themselves suddenly change – it would seem possible that we don’t actually have to end up with the sort of Windows-style-restart-the-client, after all.

Aside from the above, there are many other smaller details and issues sorted as they appeared – some of them might even further change as everything takes further shape. So far though I’m satisfied that the former client-code is finally just about paste-enough to be of some actual use instead of unrelenting pain and misery.

The next steps are for now to iron out some remaining weirds in the current minimal prototype and to further develop the actual use of data from the GUI since for now there’s really only the sector+player check, retrieval and loading of corresponding graphics assets but nothing further connected. Local movement works, of course, but it’s all local and moreover, the GUI doesn’t yet request from EuCore anything *other* than the player itself and the sector where they happen to be.

On the write-up front, there’s the structure of the .zip files for sector + cal3d models to write and publish. All is quite clear and neat there but I need to find some time for it and I might possibly do first the loading of CS meshes too.

On the iffiest client-side part remaining, the paws widgeting mess looms largest. At the moment I can’t tell if or to what extent I can sidestep it/any of it but I’m certainly not keen at all to get into that swamp even an inch more than is absolutely and unavoidably required.

June 28, 2020

Eulora Client Data Hierarchy – Finally Getting There!

Filed under: Coding,Computer Graphics,Eulora — Diana Coman @ 3:59 pm

Eulora’s communication protocol aims to be indeed as short and simple as possible while providing at the same time the full means for sending over to the client *anything and everything* at all that might be wanted now or later on, whether known currently or not. Given this broad scope and combined with the terrible state of the original client, it’s perhaps no wonder that I had a rather frustrating time with my previous attempts at pinning down a practical implementation of it: here’s for instance one attempt of defining the client-side data hierarchy and there’s its further refinement too, but those are only two cases that I fully documented in an attempt to clarify matters better – the effort was certainly not wasted but the results in both cases are still lacking and I found myself repeatedly getting tangled in all sorts in there, as it’s indeed very easy to run into circular definitions of things or get confused by the required combination of structure, meaning and representation. Nevertheless and much to my recent relief, it seems that I finally got my head around it – it took only yet another re-read of *everything* around it, previous discussions and attempts included, as well as all the previous hand-banging and failures, of course, what else.

As it happens though in such cases, once the last bits and pieces finally fell into place, it all started to make sense and fit together (although it *also* meant that half of my initial test/exploration implementation had to be thrown away; sigh). Since the actual implementation is still in the works and it’s likely to still take a while as it’s not exactly a walk in the park, I am taking this time now to try and set down my current clearer understanding of how it’s all going to work – so that on one hand I have a reference point for later on and on the other hand it’s out in the open for any further discussion and fault-finding.

For clarity and keeping it all contained here, note that I’ll focus strictly on the data transfer part of the protocol, meaning messages for File Request, File Transfer, World Bulletin, Object Request and Object Info 1. Of those, the last three are not even fully defined in the current protocol precisely as further details needed clarification – in particular the “object list” in the World Bulletin and the “object properties” in the Object Info are essentially “to be defined”. Skipping through all the intermediate attempts that either didn’t quite fully work or turned out to be in practice so clunky that I couldn’t live with them at all, the breakthrough basically came when I finally managed to set down a few basic things (that even seem very simple in retrospect, of course). To get it all in one place:

  1. There’s only *one* primitive to this whole thing and that is “object”, no exceptions. For a long time I kept tripping over “properties” and “values” and whatnots, as I didn’t quite grasp the boundaries (admiteddly blurry of times) of the different perspectives involved and kept trying to sort out or pin down too fine details before having a solid higher-level structure to rely on.
  2. Objects may be concrete or abstract. The concrete objects are essentially those with a direct graphical representation in the game (such as your character or even the terrain as a whole). The abstract objects provide all and any sorts of other information required (such as what time it is in game perhaps or which object from all those in the world is actually your character!).
  3. The whole game world (and this does include *everything* that may be requested or received through the set of messages mentioned above) is a hierarchy of objects with a single “root” node that is quite special in that it’s essentially the “null” object itself. So to “request” the World Bulletin, the client basically needs to request the “null” object. This hierarchy of objects that is a full representation of the game world can be arbitrarely deep and wide at any given time – the only guarantee is that it is indeed finite at all times but the number of levels or the number of siblings on any level can and will change, there’s no restriction on either of them (nor any need for such restriction).
  4. The hierarchy of objects given as above provides basically the *structure* of the world, effectively describing where *is* each concrete object (or where it belongs, if it’s an abstract object). As such, concrete objects can of course move about from one parent object to another (and take all their corresponding subtree with them). Abstract objects are, logically speaking, less likely to suddenly move to a different parent but there is no guarantee that this can’t or shouldn’t happen either.
  5. The full set of object types can be neatly split into TWO main categories: “leaf” types and “non-leaf” types. Realising this was big for me, as it suddenly got rid of a lot of mess in there: non-leaf types of objects can only ever contain other objects (of any types though and in any number) but *nothing else*; by contrast, leaf types of objects do NOT contain other objects (obviously!) but have instead a predefined and fixed structure (basically, values). A lot of my previous trouble seems to have come from mixing those two groups ie considering that an object type may legitimately contain both other objects AND values 2. There’s no real need for this mix that I can see and it only causes such a mess that it’s not even worth fully describing. Once those 2 types of objects are neatly separated, everything falls in place and there is both the desired full extensibility of the whole thing (one can add any number of non-leaf or leaf types, as well as any number of intermediate levels in the hierarchy anywhere at any time without breaking anything) and the needed clarity for a working implementation: as long as a non-leaf type of object is encountered, the “properties” of it will simply be again an “object list” just like in the World Bulletin message; as soon as a leaf type of object is encountered, its structure is clearly known and *fully fixed upfront* so it can be actually worked with! Certainly, it may well be that one implementation or another is not aware of some newer types, be they leaves or not – in such cases, those types will be ignored since what else can the code do anyway, not like it can use what it doesn’t know about. Note though that this doesn’t break any of the rest – it will work for and with what it knows, while dropping what it doesn’t and that’s that.
  6. All objects have a unique ID and a type, meaning that an object is effectively a tuple (Object_ID, Type_ID). In practical implementation terms, this means that the “object list” in the World Bulletin message is *always* 3 simply a set of pairs of numbers so if World Bulletin gives 5 as “count of objects”, then there will be 5 pairs of IDs following, interpreted exactly like that: first ID in a pair is object’s ID, second ID in a pair is type ID and there’s no need for separators or any further information provided.
  7. For a non-leaf object type, the Object Info message will *always* contain as “object properties” simply an object list (the text field still works perfectly fine since it’s known that there are 4 octets per ID), just like the World Bulletin. (Note though that the Object Info message may refer to more than one object and so some of those may be leaf types, too, so the properties for those will be different, see next item.)
  8. For a leaf object type, the Object Info message will *always* contain as “object properties” the exact structure known for that specific type. Obviously, a full list of those structures is in the works and will be published but the crucial thing here is that the contents of those structures are always *values*, never objects. Sure, it can very well be that a value in there stands for an id of some object but the *meaning* is very different since it is NOT about structure anymore and so that id in there will then reference an *existing* object from somewhere else in the tree instead of introducing a new object (and therefore it will NOT need to come with the type id either)!
  9. Values in the structures defined for leaf node types may be in principle of any known type, whether basic or not. From a practical point of view, the only truly special case is that of a value that represents a filename, since in this case there’s effectively a further level to it all – the implementation will have to send a File Request message to get the actual content of the file, if it doesn’t yet have it. Nevertheless, this is not any significant trouble as such.

For a practical example, here’s my current must-have basic set of object types that I think are absolutely needed in order to get the client to the stage where it finds and requests graphics assets that it doesn’t have:

  • RootObj (the null object is the only one having this type and therefore the type itself is essentially an abstraction, no need for a type id as such)
    1. SelfObj containing for now 4:
      1. PlayerIDObj – this is LEAF type with a single value, an Unsigned_32 that references the object in the world that represents the character controlled by the player.
    2. SectorObj (the id of this object will be given as top_id in a World Bulletin, too) containing for now:
      1. NameObj – this is LEAF type with a single *text* value.
      2. OutdoorsObj, containing:
        1. ZipObj – this is LEAF type with a single *text* value representing the full path and name of a zip file that contains *all* the graphics assets required for a sector. Note that the exact contents may change at later times but I don’t see this as any problem – for one thing the plan is that the client will be able to select one overall schema at any given time and that schema will determine what exact file is made available here and for another thing, those schemas will be known upfront so a client will select what it knows and therefore won’t have surprises regarding the content of this zip file either. For a long time I tripped over the trouble of defining the graphics in too much detail, quite possibly precisely because I lacked that knowledge myself. As meanwhile I sorted out this issue and I know all too well what exactly is needed, I finally realised that the only way to cut this knot neatly is to just specify a .zip file as a general “graphics assets for a logical object”, no need for more at this level.
        2. HashObj – this is LEAF type with a single Unsigned64 value that represents the Keccak hash 5 of the .zip file above.
      3. Any number of PlayerObj, containing:
        1. NameObj – this is LEAF type with a single *text* value.
        2. PosRotObj – this is LEAF type, with its value (“properties” text field in the Object Info message type) sent over the wire as 3 int16s for position followed by 3 int8s for rotation, as per the protocol specification
        3. Cal3DObj, containing:
          1. ZipObj – LEAF type, as above (see at OutdoorsObj – the big gain here is that although a Cal3D model will have an entirely DIFFERENT set of files in that zip than an OutdoorsObj, from the point of view of protocol spec and data acquisition, there is NO difference! They are simply all .zip file and that’s that, no matter what they currently contain or may contain in the future or whatever else, hooray.)
          2. HashObj – LEAF type, as above (ie the hash of the zip file, for checking that it is indeed the intended file).

The above is “all” for now – if still just a plan to be implemented. Sure, I have otherwise the concrete details of what those .zip files for instance will contain and all that but they don’t really matter all that much from the point of view of the overall data model and data acquisition – the contents are in the end of interest strictly for the GUI/graphical part itself so they’ll be handled there and only there.

Regarding the recent discussion of the potential need to be able to choose sizes for textures and so on – those I think are also best chosen separately, at a higher level, as an overall thing, so not going about specifying them for each and every texture or image file or something. At any rate, I don’t think it’s something that absolutely must be included in the spec at this time or here but do let me know if you see any trouble with any of the above, as usual!

  1. The encryption and lower level packaging part of the protocol has been already cleared up, implemented and working fine, see the SMG_Comms category for detailed documentation on it and the basic docs for a high-level overview of the client’s structure overall.[]
  2. In turn, this came from my still-not-wide-enough definition of objects, since I didn’t quite grasp that there will be objects strictly for this purpose basically e.g. there’s an object that would be fully described as graphical-model-of-Sue’s-helmet-at-night-on-the-moon or some such. Once I got my head around this, I even find it difficult to fully point the trouble I previously had with it, go figure. Though in hindsight, I suspect at least part of the trouble was the unhelpful overlap with “object-oriented” idiocy, ffs.[]
  3. There’s a reason for NOT ever having directly a “leaf” type of object in the root: leaf types of objects provide concrete values in predefined structures but they do NOT contain, nor can they contain at the same time the meaning and role that those values are to play at any given time. Hence, the root node will always have to contain at least one level of non-leaf objects and I kick myself for not realising this clearly any earlier![]
  4. More will be added to this and to most other types described here but right now this is on purpose as small as possible, to have it first implemented and working.[]
  5. I chose this as it’s been used elsewhere, though so far not for full files – just let me know if something else would work better, since I need to implement this part anyway so there’s nothing set in stone yet.[]

June 22, 2020

Eulora Client Graphics: Main Types and Formats

Filed under: Coding,Computer Graphics,Eulora — Diana Coman @ 4:09 pm

Having made by now a full graphics generator that produces everything, from textures to meshes and full animations too 1, I can also finally get out of the existing swamp of data somethings and provide instead a clear list of types, formats and files that are actually quite enough for client’s needs. Basically there are only 3 big types, each requiring a bunch of specific files:

  1. Outdoors type (or “sector”, “area”, “zone”, “world”, whatever you want to call it, the point is that it has some solid terrain, some sort of “water” in the lower parts and some sort of “sky” that should arguably be in the upper parts only but is in practice all-surrounding). This is the most involved type as it requires three parts, each with its own bunch of specific files 2:
    1. Terrain, requiring the following files:
      • heightmap.png
      • materialmap.png 3
      • lightmap.png
      • normalmap.png
      • basemap.png
      • tex_1.png 4
      • tex_2.png
      • tex_3.png
      • norm_1.png 5
      • norm_2.png
      • norm_3.png
    2. Water 6, requiring the following files:
      • tex.png
      • tex_reflected.png 7
    3. Sky 8, requiring the following files:
      • tex.png
  2. Non-moving type, aka CS mesh, requiring the following files:
    • meshfactory.csmesh 9
    • tex.png
  3. Moving type, aka Cal3d, requiring the following files:
    • skeleton.csf (or .xsf)
    • any number of .caf (or .xaf) files – the animations. Those may have in principle names matching the “type of animation” they are meant for but note that on one hand CS/Cal3d has a limited and fixed number of such “types” (specifically: idle, travel, cycle, style_cycle and action) and on the other hand it’s not all that clear what’s the point of them since the more important part is otherwise setting for each some use parameters, of which the most important are the speed interval for which that animation is relevant (hence, when it will be used) and whether to lock in the final pose or not.
    • any number of .cmf (or .xmf) files – the meshes making up this model and including the texture mapping. Note that different models have different number of meshes and there is no specific order in which the meshes are numbered either. As a pattern at the moment I’m simply using mesh_id.xmf as name for the file and loading until no file found.
    • tex.png (or .dds) – the texture to use for ALL meshes; alternatively, any number of tex_id.png so that each mesh has its own texture, supposing that this might be useful at some point.

In addition to the above, there are a few more formats that are simply one file each, used as such: icons (simply a .png or .dds file), sounds (.ogg or .wav) and perhaps particle systems that will need a SMG format since there is no file/format for them at all, only all sorts of code-side settings of this and that. Currently and as per previous discussion, the sounds are not going to be supported but I mention here for completeness the formats known to work with CS.

All image files may in principle be either .png or .dds, with the .dds format the preferable one (takes less space and requires less computations client-side). This being said though, the client should work with either/both and there is only a reasonable requirement that the size be a power of 2. Current textures are all 512×512 pixels and any change to this needs to be matched with a change to the texture mappings provided for each mesh and so on – in other words, I think the correct approach here is simply to bundle together as above the mesh definition with the texture it is meant to use and don’t even bother otherwise to explicitly provide whatever number of different sizes – the size of an image is anyway included in the file so whoever is interested to extract it may do so directly from there.

The above description of types and formats marks a fundamental shift in approach and since this cleared up for me only gradually as a result of all the work sunk into the graphics and client side since the beginning of this year, I’d rather set it out explicitly here. The old client’s approach focused on loading everything upfront and keeping it all in memory, based on the assumption that there are somehow great gains to this by means of extensive reuse of assets once loaded. In practice I fail to see those gains but there is otherwise ample cost clearly visible and always paid upfront 10: fixed and difficult to expand set of assets, long – and moreover, getting longer ie *worse* with any extension of the assets!- initial loading time for the client as a whole, significant overhead required to support the reuse fantasy because otherwise there’s precious little reuse when it comes to meshes and characters and even textures. Basically the old idea is of having a central catalogue of all graphics assets and then referencing those from everywhere else. By contrast, my current approach has no such central catalogue and relies instead on each element that requires some graphics assets to know where to find them and possibly have them in its own directory. Essentially, the current approach considers disk space cheap (though NOT network communications – note that assets may end up duplicated locally but in principle they don’t *have to* be also sent 1000 times over the wire; it can very well be perhaps the job of the data acquisition module to duplicate files if/when needed and/or set them or link them where expected), discards the reuse pretense and relies instead on minimal overhead as well as minimal, on-demand loading (potentially coupled at implementation time with aggressive clear up of unused resources if memory gets clogged otherwise).

While the above is not yet set in stone, it might very well soon become set, so let me know if you see any trouble with it or if there’s some better way to go about it that I just don’t see yet.

  1. For the curious, *all* this stands currently at 5581 lines of code+comments+tests+png writer + convenience scripts. No, I did *not* forget any 0 at the end and no, it doesn’t require external libs, platforms whatever-else, it’s standalone, C, gawk and bash code only.[]
  2. The .dds format is in principle better but in practice all image files may end up either .dds or .png so the client side will have to make do with what is provided, perhaps run its own conversions if it has to.[]
  3. Also known as alpha map, if that helps you any.[]
  4. These are the materials that get used/applied depending on the height and on the basemap – while logically speaking I would just say any number of materials should work, in practice the existing terrain plugin wants exactly and precisely 3, not more nor less. This being said, the format from my point of view will be tex_number files and load as many or as few as your client can use.[]
  5. Those are normals for the corresponding texture and in principle they are optional though the terrain will likely look worse without them.[]
  6. The current implementation does require a bunch of other settings such as reflection coefficient and whatnot but I think that’s strictly a client-side concern so entirely locally-set, nothing to do with getting it from the server.[]
  7. This is a texture to be reflected by the water and it’s optional. Currently this is the sky’s image.[]
  8. Note that the current implementation does require all sorts of other “options”, from picking the geometry of the sky (!) to a whole bunch of shader variables. Nevertheless, I don’t see why would any of that be a concern outside of each client – so they’ll be set locally, change your client as you want it if you are fed up with the sphere-sky or something.[]
  9. XML file in CS “meshfact” format, which can be loaded directly with CS’s loader and then instantiated as and when desired. It contains the following information: position of all vertices, normals of the surface at each vertex, texture coordinates at each vertex, triangles making up the surface. These are effectively listed as such, since this is what CS expects. Note that the format forces the mesh to include a fixed texture mapping, but any texture can be used at load time – how well it will fit or not the mapping is a different matter.[]
  10. Come to think of it, this does strike me now as very much the typical problem: paying upfront a significant price for…. not missing an opportunity that never materializes though, huh.[]

May 5, 2020

Disc Included, Spelling Approximate

Filed under: Coding,Eulora — Diana Coman @ 9:35 pm

As it took a bit of a hunt to still find somewhere those older and more useful editions of various reference books on Computer Graphics, I shrugged at first at the note attached to one of them – “disc included”, it said and I couldn’t really care at all whether the disc in question was indeed still included or still to be found or still working or even still anything at all. And as the books came, I took my time to look them over, noting even that they don’t make perhaps such a bad name-day companion after all and it’s no hurry and all that. But an honest shopkeeper is an honest shopkeeper and his notes can be therefore trusted, for included it turned out to be, for all the surprise that the image of this most humble disc in question may nevertheless stir perhaps in some younger readers of mine:

disk_1_640.jpg

Included it was therefore, as you can surely see for yourself above, if not a disc but a disk(ette). For future record and for even younger readers, above depicted is a 3.5 inch floppy disk that belongs with this book from 1994, basically the most advanced and latest floppy disk model that there ever was. The memory trip it stirred insists that those old computers I still supposedly have in an attic somewhere have the drives required to read 3.5 disks as well as those able to read the older, larger floppy disks – 8 inch, if memory serves. At any rate, I would not be terribly surprised if this disk is perhaps still working too. Only I don’t quite think I’ll rush now to that old attic all those kilometres away to dust off one of the old computers, see if any of the CRTs still light up and then try it all out.

Let me know though if you ever want to try it out yourself, for good times’ sake or for anything else at all – the disk is here and it remains, indeed, included!

March 24, 2020

Spraying Mandelbrotian Graffiti on Euloran Surfaces

Filed under: Coding,Computer Graphics,Eulora — Diana Coman @ 7:53 pm

This past week I got some decent practice with virtual surface vandalisingtexturizing after polygonizing – and that includes some agonising, namely over CrystalSpace’s idiotic requirements for texture if it’s to do anything at all with it.

Sure, I knew already that CS can’t do anything with a texture’s procedural specification as such and requires instead the pixel by pixel description of the pretty picture to paint on whatever surface. This much was no surprise indeed. But it turns out that there can be (and therefore is) even worse than that – CS *also* has no idea nor interest to map by itself a given texture to an object, even poorly! Basically it’s not enough to feed it all the pixels of the texture – you have to chew them too! And who cares that such chewing further forces the fixing and specifying of “how to paint” in the file that supposedly is instead all about “what shape” this is (aka the mesh description file that has to include also the texture mapping). Eh, such unheard of troubles and scruples that haunt my nights and days around here, let it all rot and fester in one place, what’s it to artists anyway and not like anything else matters if it’s about pretty pictures on a screen, right?

To put the above in plain terms: CS allows you in principle to apply any texture you want to a mesh (aka polygonized surface) *but* for this to do anything, the mesh’s definition has to contain also a fixed mapping of each vertex to a corresponding point of the texture (and no, CS manual doesn’t go into such details)! The image that is used as a texture is considered a “texture map” and has a standard “space” going from (0,0) to (1,1), representing the top left corner and bottom right corner of the image, respectively. The mesh definition then has to specify for each vertex, a (u,v) tuple with the u,v values in the above space for direct mapping; negative/ out of range values will result in tiling of the texture or wrapping around (at least in theory, I haven’t bothered to fully explore this part as I do not want either tiling or wrapping of the texture).

The above means in practice that, although you can supposedly change the texture on the fly on a mesh, nothing will fit anymore unless the two textures you switch between are *both* made precisely to fit that mesh in the same mapping and all that. And there isn’t any sane default for a fit – in my naivety I thought that it would just do some default mapping of the whole texture on the whole surface but practice and this week’s dive into the code reveals that there’s no such thing attempted, no. Instead, if you don’t specify within the mesh any mapping of a not-yet-chosen texture 1, it will “apply” the texture aka the 0,0 point of it and nothing else – meaning it’s the colour of *that pixel* used… everywhere. For the same money, you can just use a solid colour directly anyway, what the fuck’s the point of specifying a texture and loading an image and all that jazz?

The obvious cause of the above insanity is the fact that you are not expected to generate anything at all – you are expected (with a view to force fed already) to “export” everything from one of those crutches for the mathematically challenged (such as Blender). And in there you manually specify some “seams” for a character so that an algorithm unwraps then the skin of the character through a cut along those seams and lays it flat in the texture file, while producing and saving also -generally by means of at least 3 renderings of the thing from different perspectives!- the mapping of vertices to the points in the texture space. What do you need else or more or even less, right?

Anyways, this solved the previous puzzle of those solid-colour surfaces of last week and of Cally’s solidly-coloured skin no-matter-what. Cally’s meshes do not contain any texture mappings so yes, either you do your own or you paint her directly at runtime or you just make her some solid colour of your choice. And for extra bonus points of wasting time, the darned thing wants all those texture coordinates specified in the xml file as a render buffer with the *precise* name of “texture coordinate” – failing silently, maddeningly and quite characteristically if you do the huge mistake of calling that, for instance, “texture coordinates”. As I did, of course, at a cost of a whole morning and assorted cussings in all languages that came to mind. Here, have some pictures of the hyperboloid that silently ignored the texture mapping (the one with spots – the very last pic in this set- is where I added the same mapping to be used for *normals* of the texture and that *worked* because there I got the name right to the letter…) for reason of one ‘s’ too many:
mbrottex_5_640.png
mbrottex_6_640.png
mbrottex_12_640.png
mbrottex_18_640.png

On the bright side, I didn’t even try anymore to contemplate the obvious what-the-fuck questions pouring out from the above. Contemplate them on your own time and your own dime if you really have nothing better to do. What I did instead was to update my awk formatting script so that it spits *also* the required texture coordinates for each and every vertex. After all, since it does all sorts to fit CS’s love of xml, why not this too? And I kept it short and simple: considering the texture square in size, it just maps the (z,x) coordinates of the shape (considering the actual interval that they spawn for the given surface, so not idiotically from 0 or some such nonsense but between their observed minimum & maximum values) to the full (u,v) space of the texture and that’s it. Why the (z,x)? Why not? Sure, there’s plenty to play around with there but at this stage I wanted to have something working and working fast, not like there’s any clear reason as to how this mapping would even work “best” or what that “best” even means in such context. I did in fact try it at first with mapping (x,y) (hence, ignoring z) but the result looks less interesting to me than the (z,x), so I stuck to the more interesting mapping. Here’s how it looks with (x,y) vs (z,x) mapping (the texture is a full Mandelbrot, coloured black inside the set and yellow/blue outside, based on normalized number of iterations, see next paragraph for more on the texture generation itself):
mbrottex_17_640.png
mbrottex_13_640.png

The easy part linked to all the above was to generate *a* Mandelbrot texture. Note the emphasis there because the Maths is easy but the devil in it goes by the name of domain colouring. Essentially the whole thing with a Mandelbrot (or fractal in general) pretty picture is how you pick and apply the colouring scheme (well, ignoring for a moment that you can further zoom in/out if you want to capture some specific bits/parts that might look more interesting than the whole; for any definition of interesting, for any combination of all the various choices, of course). And domain colouring is a whole…domain in itself, you know? Not to mention for that matter that Mandelbrot might be even used as a more general name for fractals and moreover, one could even look at the fractal dimension as the more general approach to procedural generation of anything, from texture to shapes – and get lost in *that* rabbit hole if it’s more appealing than the rest of rabbit holes around.

Mandelbrot’s pretty fractal aside for a moment, I stepped daintily over the rabbit hole of fractal dimensions and domain colouring at this stage 2, simply picking instead *a* colouring scheme by means most undemocratic – because I find it ok to look at. While I don’t mind changing it and/or implementing some other specific colouring scheme you might have in mind, at least there is something implemented and visible so far, to start from. Other than this, do tell me if there’s a request for further exploration of any of the abundant rabbit holes around (e.g. domain colouring or the fractal dimension as such). Meanwhile, here’s what the Mandelbrot texture looks like in a few colouring (starting with grayscales) versions and then how the blue/yellow one looks on some surfaces that I got to play around with:
mbrottex_10_640.png
mbrottex_8_640.png
mbrottex_9_640.png
mbrottex_15_640.png

Using the last texture in the set above, here’s how it looks applied to various surfaces I tried out as part of attempting to get a bit of a better understanding of that part too:
mbrottex_7_640.png
mbrottex_1_640.png
mbrottex_2_640.png
mbrottex_3_640.png
mbrottex_4_640.png

With the textures at least figured out as above, I’m currently working on figuring out a way that works for finally setting up a full char made out of meshes “grown” around an arbitrary “skeleton”. While implicit surfaces and modelling with them is of course yet another domain by itself, I’m not getting lost in it either, no. I still need though to get a better understanding of what works and what doesn’t (e.g. some type of surfaces have the known problem of “bulging” at joints) and how exactly that happens & links to my useful but limited polygonizer because the naive definition bumps into all sorts in practice and there’s no substitue that I know of for playing around with actual surfaces and all that. Depending indeed on the type of surface, the “bone” can even work for instance as either medial axis or control points or pretty much anything under the sun. But some concrete pictures will most likely say way more about this than all the half-digested theory can at this point so I’ll just go ahead with it, unless requested otherwise.

Based on the above plan, the next write-up will hopefully include even more mandelbrotian graffitti but on attempted meshes-on-a-stick (aka “skeleton”) or at least this is what I’m currently aiming for.

  1. I’m practicing laughter, serenity, calm and pure zen. It’s not working all that much but that’s why I’m …practicing it, ok?[]
  2. Though a 1994 edition of the reference book of Ebert, Musgrave, Peachey, Perlin and Worley made it to my shelf.[]

March 17, 2020

Shape Up Yer Polygons, Eulora!

Filed under: Coding,Eulora — Diana Coman @ 6:24 pm

~ This is a mixed up article with serious pictures for easy reading and silly text for hard staring. ~

Following last week’s discovery of the missing link in CS’s capabilities (no meshing and no tesselation implemented as such), I took another dive in the ocean of words on the matter, reviewing papers and theses and software monstrosities by the boatfull 1. At any rate, the core ideas are very few indeed: the focus is almost exclusively on either “assisting the artists” (in that dumb sense of catering to their convenience rather than to any attempt at excellency or even plain exploitation of the possibilities of what is a different environment than that of the traditional drawing/sculpting) or otherwise reconstructing surfaces from a *lot* of data that comes from scanning objects so basically yet another facet of that great new definition of success as approximating unanalyzed data.

The algorithms are mainly variations on delauney/voronoi refinement, marching cubes/triangles/particles for surface reconstruction and otherwise the whole of bones and skeletons pretty much stem anyway from Blum’s medial axis – that is indeed the topological skeleton of a shape but is also precise enough to result in practice in a lot of small bones if it’s taken as such. As it tends to happen in the best of cases, when I was just about to run out of oxygen for good on this, I finally turned out instead something quite promising and much in the same way as before – among the dull and broken “let’s tweak this to change that”, something glimmered all of a sudden, in quite the same way as Blum’s earlier beautiful paper on the medial axis – this time it was Bloomenthal’s paper 2 that went straight and unapologetically to the heart of the matter, from the very first sentence: “An algorithm for the polygonization of implicit surfaces is described and an implementation in C is provided.” And wonder of wonders, what this amazing first sentence promises, the following words actually deliver, too!

The algorithm itself is just about as simple as one can make it for the task at hand: the core idea is to use cubes of equal sizes for partitioning the space, to gradually build such partitioning through step by step expansion from a starting point on the surface until the whole surface 3 is contained in the obtained collection of cubes and then to decompose each relevant cube’s face into the corresponding polygons (triangles for my use). The directions to expand to at each step are easily identified by considering the sign of the implicit surface function at the corners of each face of a cube. And taking advantage of the regularity of equal-sized cubes as partitioning cells, the different cases and their corresponding meaningful expansion can be precomputed and then simply used. Note that in all of this, the cube’s size is indeed fixed but it’s a parameter of the algorithm – basically larger cubes provide less detail and that has the advantage of fewer vertices and corresponding triangles but also less smooth resulting faces/shapes that can run into very poor approximations of the actual surface especially where it curves.

In 4 neat pages, the paper goes through a clear description of the method, a discussion of some implementation choices for simplicity, flexibility (e.g. the intentionally black-box approach to the implicit function definition at polygonization time so that one can use a continuous function as intended but also anything else really, as long as it accepts a set of 3 real numbers and provides a positive/zero/negative result) and speed (in particular the hashing of values -such as vertices- that are re-used during the building of the collection of cubes containing the whole surface), several concrete examples of some implicit functions (a torus, a jack, a blob, a combination of two tori) and a brief discussion of potential issues (especially if the implicit surface function is not C1 continuous since in this case various artefacts may appear, such as truncated edges or jutting parts).

The promised code is indeed included – if in a horrible form since it’s literally dumped in a .pdf at the end, the stuff of horrors as far as code is concerned. Nevertheless, it is *plain C* indeed, it is less than 1000 lines in *total*, even including some examples and whatnots, it is clear, commented, properly named and everything else that requires apparently rediscovery nowadays and it was not even mentioned as anything special back in the 90s. Anyways, I read it at first half-expecting it’ll turn out to be just a “proof of concept that kinda maybe works on narrowly chosen examples”. As I read, I started to realise that it might actually be for once, something useful. And then I went ahead and picked the parts that I need, wrote them down in a .c file, compiled the whole thing (imagine this, *nothing* required other than a c compiler, ok?) and then started playing around with it. Ah, the joy, the pure and proper joy of *finally* having something to play around with rather than suffer through, for once.

Despite all the wonders above, the implementation as it stands has its own limitations – although at the moment I’m not even sure if those limitations are not in fact quite useful. The most important one is that a run can fail in 2 cases: if the starting point is not close enough to the surface or if the polygonization runs out of allocated memory for the hash tables it creates.

The first trouble is a bit of the price paid for the simplicity of the algorithm: the first cube built around the starting point has to include some of the surface or there’s no way for the algorithm to know which way to expand to discover the rest. I don’t think this is much of an issue since yeah, I plan to build my implicit surface functions so that I can at least find ONE bloody point on the surface, one way or another 4.

The second trouble related to running out of allocated memory can in principle be addressed most easily by expanding the maximum allocated memory (though this may be a bit more involved than simply changing a number given as it’s not directly exposed as a parameter/variable as such but set within the type definitions and so a change requires careful follow up of all parts that may require change subsequently). Nevertheless, I won’t hurry to do this change because it *also* means that the resulting polygonization will have therefore *more* vertices and triangles. And in turn, it’s not all that clear that I need or even want more vertices and triangles anyway (and certainly not an infinite/practically uncapped number of them, wtf). So it stays as it is for now, I’d say.

Armed with a working polygonizer, I then looked at the eternal problem of matching the output format of one part (the polygonizer in this case) to the input format of the next part (the client ultimately but possibly the viewer of test meshes first). As the starting point for this was to generate characters, the format needed would logically be the cal3d mesh format since each part of a character in the client would have to be in the end exactly that, a mesh in cal3d format including the “influences” of those bones-that-are-not-quite-bones. Except of course, at this stage there’s no fucking influence anyway because I’m just generating the mesh, not the animation and so the “bone” is at best an entirely abstract and rather optional idea, especially while still just trying out the algorithm and the whole. So yeah, I wrote a few lines of awk 5 to just do *both* the cs and the cal3d “mesh” format out of the polygonizer’s output – not like it’s much bother anyway and look at that, I can further *also* do any size adjustment on the mesh at the same time, if I want to (and I do want to, for a reason I hadn’t realised at first).

The reason why I did both cal3d and cs format is that visualising just one mesh is in fact more straightforward as such rather than as part of a full cal3d model. And in turn, the cal3d format requires all those bells and whistles that are in fact just stored further calculations meant to support the animation, nothing to do with the shape itself. So let that wait until I have *what* to animate and let’s see some pics already. First of all and for comparison with the results of my polygonizer, here’s some primitives generated with CS’s own internal code that simply has a fixed recipe for a few regular shapes, of which you can admire below what should be a cone (looks more like a pyramid to me, due to how the texture got applied to it), a cylinder (seen directly in the client, through Cally’s eyes of sort) and a small ellipse (seen in the viewer). Note that CS itself complains about “degenerate faces” on the cone – in practical terms this means that the collision detection system is likely to have some amount of trouble when that object is involved:
shapes1_11_640.png

shapes1_13_640.png

shapes1_14_640.png

Moving on to something more interesting, here’s a collection of various experiments with the polygonizer and implicit surfaces. For starters, here’s how a botched torus looks like – I had messed up the equation of the function and so there you go, have some funky wedges, why not:

shapes1_1_1024.png

And here’s a blob (made out by combining in the same equation the 3 parts because such are the wonders of Mathematics, yes, you can do what you want, with just an equation, how wonderful) in the extended, cvasi-infinite viewer (I had gotten fed up with the tiny stone cell and moreover, I hadn’t yet figured out that the lights were all of a sudden all wrong and as a result making everything that brownish tinge, texture be darned; sigh):
shapes1_2_640.png

Next on the list and placed in the more familiar landscape of Eulora, a shiny swimming aidtorus 6 (the texture is that of some water and it was part of my attempts to figure out the textures too – that is still ongoing because of course it’s not quite working as expected, grrr). The weird handle on this torus is a bit…weird. Basically the origin of the torus seems to be considered on the surface for some reason. It’s not a big deal, in that it can be easily fixed, of course, but since it’s not exactly tori that are going to make a character, I didn’t bother all that much to track down the full backstory of how this particular wedge here happened – just be aware that yes, it can happen:
shapes1_10_640.png

Since after a while even a torus is quite boring, I decided to see how the polygonizer behaves on an unbounded surface – especially since there were no examples of such things in the original paper. One short equation (and a few adjustments to finally streamline my process so that all it takes from generate to view is running 2 scripts & starting the client), I admired hyperboloids of all sorts (and of course they remind me of petroleum refineries, what else):
shapes1_3_640.png

shapes1_4_640.png

shapes1_5_640.png

shapes1_6_640.png

shapes1_7_640.png

shapes1_8_640.png

shapes1_9_640.png

Probably you are wondering now what happened exactly in the last picture and what are those seeming spikes – is that even still a hyperboloid at all? Allow me therefore to assure you that it is indeed still a hyperboloid, no matter what your eyes may tell you. Moreover, it’s just as much a hyperboloid as the previous ones were – it turns out however that CS tends to have trouble with the hollow, thin and unbounded surfaces that I generate. When seen from some angles, they vanish entirely and I suspect the trouble is that CS expects in fact two “layers” of triangles to be able to show such a surface as expected – while my polygonizer provides the outer surface of the thing, it does not add to it an inner surface so the “inside” is …invisible. Is this a problem worth addressing right now? I’d say it can wait for now, even though indeed, look at what mess it makes of what is effectively the simplest “wrap” around an imaginary line-“bone” aka a …cylinder (if defined implicitly, of course), what:
shapes1_12_640.png

The above experiment with unbounded shapes set my mind to rest that yes, indeed, the polygonizer can handle those. It also revealed more clearly the need to tweak both the two parameters of the polygonization depending on the shape (ie the size of the cubes and the maximum number of cubes to explore on each side) and the size of the shape after this step. This is because the *size* of the resulting shape depends on those two parameters too. And in turn, the *part* of the shape obtained will depend on them so basically one might need to tweak more than it seems at an initial naive look. Nevertheless, those are very easy tweaks really and at least so far I don’t quite see much trouble with them: I’ve already changed the code to just accept as parameters the two parameters for the polygonization so I don’t have to recompile it every time those change; and the script porting the stuff to cs & cal3d gets its own parameter to apply a scale factor as desired so that the *resulting* mesh is exactly the desired size; put together, this means that I can in fact create meshes as big as it might be required to capture nicely whatever details and *then* I can painlessly shrink them too, without loss – obviously, within some reasonable limits since if I shrink everything to a single point, there is arguably some loss of detail, indeed!

As for some fun I had after sorting out all the above, here’s a very pointy implement for all your impaling-in-walls needs! It’s defined of course through it’s implicit function or in other words, you can certainly impale yourself in an equation, no trouble:
shapes1_19_640.png

The next step on this is to sort out the textures already: the trouble being again a bit of a missing link since CS expects that the texture is an image file rather than an equation/function. I explored already to some extent what I could do there – theoretically there’s a way to “write” the texture at run-time ie directly into the render buffer of the thing. But all experiments and even CS’s example on this worked in the sense that yes, I can write whatever I want directly to the screen – unfortunately, directly using the screen’s own coordinates too rather than the object’s coordinates, which is rather a pain (and doesn’t quite make sense if I’m supposedly writing to the object’s buffer as opposed to a *global* buffer, wtf?). Alternatively, I found also that CS conveniently has even a way to write a texture to an image file but then of course, I don’t know how to write & especially how to map such a file to the vertices of a generated surface so that it’s neither just a stretched thing nor a tiled thing – the way the mapping of the texture works exactly is quite a pain to fully get because the “explanation” is not helping me much with understanding the approach itself, sigh. Nevertheless, I expect I *have to* figure this out and quite in detail at that, sticking textures for the win and all that. And then of course, I’ll need to do and stick the Mandelbrot, yes.

Once the above is sorted, I guess the next thing would be to write the surface function a bit more interesting than the above, generate it for more than one bone too and then – the UGLY part – do also all the precalculations and futzing about that the cal3d format requires in order to put the shapes at the right place, sigh.

I expect the above will still take some significant time, there doesn’t seem to ever be a very fast route for what I need to do – or not one that I can yet see.

  1. I won’t name all of them, nor even spend more time to do the full write-up, at least not for now. It’s not that I can’t find what to say or that I don’t have a pile of notes on them anyway – it’s simply that I’d much rather not go through that pile of notes again, if at all possible, thank you. To unload though for now, the names I’d mention at all are quite few anyway and go in a rather obvious order from the fundamentals to petering out on the edges: there’s Blum’s medial axis (1967); there’s Bloomenthal’s work on skeletal design of natural forms (1995), including the algorithm I’m using so far; there’s Shewchuck’s work -starting with his thesis (1995) and building up from there- on Delauney refinements for mesh generation and his useful lecture notes at least; there’s Persson’s thesis (1997) on mesh generation from implicit geometries, which includes the attempt at mesh generation based on a truss-model; there’s a readable book by Edelsbrunner from 2001 on Geometry and Topology for Mesh Generation; there’s perhaps some of the earlier work (2001 and earlier) by Amenta on approximating a surface (“power crust”) based on the medial axis transform and a union of balls – effectively an approximation of the medial axis itself. I’ve added the years for a reason but by now I’m quite satisfied I have obtained a reasonable map of the domain both in names and in time coordinates really – if you want something usable and useful as opposed to something big, unyielding, overly complex and utterly narrow in its application, your destination has to be the ’90s at the very latest.[]
  2. “An Implicit Surface Polygonizer”, Jules Bloomenthal[]
  3. If the surface is bounded – like a torus say, this is obvious enough. If the surface is unbounded, then the “whole surface” is simply defined based on a bounds parameter provided as the maximum number of cubes to explore in each direction from the starting point. Simple, effective and to the point, it’s as if the author actually wanted to make something truly useful, you know?[]
  4. There is of course also the fact that any run will polygonize a connected surface ie not disconnected parts but I don’t quite see this as any sort of limitation really – wtf is that “disconnected surface” anyway, if it’s disconnected then it’s made of 2 parts and if it’s made of 2 parts then each part can be treated as one surface and look that there’s no problem anymore and we can actually get something done instead.[]
  5. By now, I almost have a sneaking suspicion that if I ever get to actually play Eulora myself as opposed to programming its server and its client and its graphics and ~everything else, I’ll play it in awk already.[]
  6. And I can’t look at that thing without instantly hearing this song of Ioan Gyuri Pascu:

    Mi-au zis baietii:
    Vezi ca fata asta-i
    Un element inabordabil
    N-ajungi la ea, oricate precautii
    ti-ai lua in prealabil
    si eu, care nici macar
    Sa inot nu stiu
    Ce sa fac?
    Pentru un prim pas
    Musai sa-mi cumpar un colac.

    Mi l-am si cumparat
    Cu grija l-am umflat
    Usor in apa am intrat
    Si mandru am cantat

    Hei tu, mi-am luat colac
    Sa vezi acuma cum devine marea un fleac
    Hei tu, mi-am luat colac
    Sa vezi acuma cum devine marea un fleac

    Scena de film:
    Eu daruindu-i cu amor
    O stea de mare.
    Si m-am trezit
    Cu masca de oxigen
    Pe fata, la reanimare
    Ea, doctor in halat
    Cu o seringa imensa in mana
    Mi-a zis: halal colac baiete
    Noroc cu medicina romana!

    Acum ca te-am salvat, mi-a zis:
    Mai canta-mi inc-o dat’
    Refrenul acela minunat.
    Normal ca l-am cantat.

    Hei tu, mi-am luat colac
    Sa vezi acuma cum devine marea un fleac.

    I-am zis: stiti domnisoara
    La un moment dat
    Credeam ca
    M-am inecat in mare
    Habar n-aveam ca aveti
    O privire asa cuprinzatoare.
    Mi-a zis: hai lasa textele
    Galanteria nu-i intotdeauna un atu.
    Mai bine ofera-mi floarea de pe masa
    Ia-ma de brat si spune-mi tu.

    Si brusc m-a sarutat
    Apoi s-a maritat,
    Bineinteles, cu alt baiat.
    De fapt, am inventat.

    Nimic, nimic din ce v-am cantat
    Nu are baza
    Desi, desi tare, tare
    M-as fi bucurat.

    Ma rog, asta-i muzica,
    Frumoase lucruri
    Regaseste omul in ea
    Da, da, da’ ce frumoasa-i muzica
    Mai ales cand omul regaseste
    Atat de multe lucruri utile, educative,
    Frumoase si morale in ea, vai!

    Nimic, da’ nimic, nimic
    Din ce v-am cantat
    Nu are baza
    Desi tare, tare
    Si va spun, pe cuvantul meu
    M-as fi bucurat.

    Ma rog, asta-i muzica
    Pe cuvant, atat de inaltatoare
    Lucruri gaseste omul in ea
    Va spui, eu, vai de mine…
    Asa-i de frumoasa
    Si de educativa…

    []

February 18, 2020

Strutting, Waving and Skin Sharing (Eulora’s Defaults)

Filed under: Coding,Eulora — Diana Coman @ 4:33 pm

Figuring out the way to make default Cal3D sprites (aka animated characters) and their animations work again in Eulora’s client turned out some unexpected pockets of sanity in CS/CAL3D, once the pile of rotten PS on top of this particular bit managed to finally get on my nerves to such extent that I fully sidelined it – if not yet removed as such (mainly because I didn’t spend the time to fully follow what else might depend on it *being there* for the time being). Nevertheless, the overall evaluation of this part as likely to be more time consuming and iffier than previous parts remains valid – it took this whole past week to figure out quite important parts of it and although I have a whole pile of findings to unload here so that I can move on more easily, there is still a lot more that needs to be done and none of it looks in any way easy or even all that *clear*, no matter how you squint or glare at it. Leaving the squinting and glaring for the very end though, I’ll go first through the illustrated guide of what works so far and what I added new since last article in this series.

Aiming this time to set in stone a default character rather than just have something to move about, I looked more closely at what concrete options there are currently – in the deployed client as well as in CS and Cal3D. The two Euloran characters (male & female aka Boxy & Foxy) have a few advantages, namely that they are made for Eulora and that they have some items of clothing that suit them reasonably. Nevertheless, both the characters themselves and the clothing are not really suited for any defaults, because they basically are too monolithical to start with – the body/clothing parts are already linked together so that one can’t switch/mix/match as required for various changes. Moreover, there are only 2 animations so not much scope to even test all sorts and – even more surprisingly to me – the binary files failed to convert to non-binary with the default converter that Cal3D ships with. So far I left this with a question mark next to it and didn’t investigate further since it’s not totally clear that there is much to get from getting to the bottom of it right now.

Given the above lacks of the current euloran models, I turned again to Cal3D’s test character, Cally, who has at least an abundance of body parts – some of them extremely perky parts, too. Compared to the euloran characters, Cally is huge though (100 times bigger!) and moreover in a different initial position but such differences are easily corrected with basic parameters when loading the model’s files: the code can rescale, rotate a model as desired and set flags regarding the position so that further transformations don’t end up flipping it all back to the original undesired stance or something 1.

A few unexpected surprises came from the sidelines as they usually do. First of all, CS is incapable of handling properly collision detection when animated sprites are involved so it wraps instead everything in bounding boxes that are big enough to contain the character fully at all times – basically the collision happens with a cube of personal space within which the animated character moves. While currently there’s little concern from this, it still sounds rather silly to me and it will probably make for relatively weird char to char fights and the like but each problem when its time comes and all that. Second, while Cal3D has its own format for materials, it turns out that CS can NOT load that format and therefore one HAS TO use CS materials even for Cal3D sprites! So to get it going for the time being, I simply borrowed Foxy’s skin and set it on Cally – since it’s anyway a basic texture otherwise, it turns out that skin sharing and borrowing works just fine at this level:
cal3d_01_4_1024.png

Since skin otherwise comes with the eyes in it too 2 but you wouldn’t be able to tell that if you looked at Cally, I tried on as well other materials – namely the ones otherwise used by the current client for Foxy’s dress and Boxy’s jacket. They simply turned gray and blue on Cally and I suspect quite a large part of it has to do with size and lack of specific cloths shader perhaps, but it’s not something with high priority right now – perhaps later on when I actually have some sort of clothing to speak of. For now, here’s Cally with gray (dress material) and blue (jacket material) skin, respectively:
cal3d_01_1_1024.png
cal3d_01_2_1024.png

For my peace of mind and thorough investigation of character’s skin at this stage, I did try on the character-specific shader (there is one – it focuses mainly on lighting) but it made ~0 difference on all three different skins so I took it out again and stuck to the basic material with one single texture. The main reason I tried this shader specifically is mainly the fact that Cal3d’s specification of materials focuses as well on lighting – basically it defines light-reflecting properties of the material and since CS doesn’t import them directly, I wondered for a minute if they’d do anything specified the CS way aka through the corresponding shader variables – empirical results suggest they don’t really do a lot really, just tiny adjustments that seem hardly worth the whole mess otherwise.

Before moving on to the animations themselves, it’s worth for my future self to set down clearly in here the way Cal3D sprites are made and managed within CS, since the documentation is extremely brief to the point of missing in parts, skipping details otherwise and generally giving only the bare minimum at most. CS essentially wraps Cal3D’s own classes so that it provides an interface that is at least similar to all the other meshes and factories and similar graphical elements. Moreover, there is a way to access directly the wrapped Cal3D model but apparently that’s more of a rake set at a good angle to take your eyes out with, since it comes with the clear statement that modifying the Cal3D model directly comes with a near guarantee of getting it out of sync with the rest of what CS knows of it and therefore making it all a huge mess. The only way I can see this is as a sort of passive-aggressive programming – I never even knew such a thing was to be a thing, what can I tell you.

Leaving aside the direct access that is unlikely to help otherwise in the least, Cal3D provides a factory that is meant to store *all* available & possible parts and equipment for a whole class/type of character 3. Seeing this in the best light, the Cal3D factory would contain all the lego-like parts that are compatible with one another. The best light is not exactly what one gets in practice though, of course, but at least the intention and potential are there, let’s say 4.

The very core of a Cal3D factory is the skeleton – basically all instances made of the same factory will share a common skeleton structure – everything else can in principle be different from one instance to another, but not the skeleton. On this skeleton, each instance can show a set of component meshes (basically body parts) and attached meshes (basically stuff, be it tools or cloths, that is attached via the sockets mechanism – each socket specifies the exact triangle in the host mesh where another mesh can be attached; this requires of course the host mesh to be defined through triangles at the very least). Both component meshes and any attached meshes that are equipped at any given time are selected out of all those available in the factory. Each instance of a Cal3D factory aka an actual character active in the game has its own state and can therefore keep track of its own set of meshes that are active at any given time, while also being able to switch meshes at will – in theory as I haven’t yet gotten around to see this in action, mainly for lack of *alternative* meshes for the same place.

On the sad side, I have to note that all my attempts to actually use existing meshes as parts of an overall puzzle game failed miserably – basically *any* use of a mesh part from one model to another resulted in a segfault in the Cal3D lib. I’ll probably have to investigate this further to get to the bottom of it in the sense of figuring out what is required exactly to produce “matching” mesh parts – and what makes them incompatible to this extent – but I suspect it all boils down to the bones they reference really, meaning that the skeleton itself will likely to have to be the main fixed part around which perhaps the rest can be allowed more flexibility.

The skeleton is so crucial to a Cal3D sprite mainly because everything depends on it, from component meshes’ position to animations that represent actions and/or movement of all sorts. The skeletal animations list the ways in which bones affected by the desired movement change position. In turn, bones influence in principle any connected/attached meshes and may also cause deformations/influence cloths or the like – though this part certainly needs more investigation and testing as at the moment there are no examples to look at nor any clear documentation as to how exactly everything works there.

At any rate, the skeletal animations as defined for instance for the Cally model work perfectly fine as expected – once PS is taken out of the loop and the velocity interval is set correctly when each animation is loaded. Cal3D activates animations based on the velocity of the sprite at any given moment and the velocity intervals in which each animation is meant to be active. In principle several animations can therefore be active at the same time and Cal3D claims to be bright enough to blend them smoothly and taking into consideration priorities too – though I haven’t yet seen this in action. For some basic animations, here are some screenshots of Cally in action strutting about the island, walking, waving and in mid-kick (note that the animations in game look even better than in the stills – sometimes the screenshot was basically not quick enough and got slightly messed up in between frames):
cal3d_01_5_1024.png
cal3d_01_6_1024.png
cal3d_01_7_1024.png
cal3d_01_8_1024.png
cal3d_01_9_1024.png
cal3d_01_3_1024.png

In addition to skeletal animations, Cal3D claims to also provide support for morphanimations. As far as I understand them currently, morphanimations would be required for more fine-grained movements/changes to a model, especially those involving parts that don’t rely on skeletal bone movements as such – in practical terms, facial movements and expressions first and foremost. At the moment this is a big unknown since not even Cally has such advanced stuff and so I guess I’ll have to… just figure it out, oh joy. On the notes-to-future-self side, there is some reasonable relevant theory writing on this, especially with respect to a supposedly robust way of reusing morphanimations across different models: the core idea is that there is a “neutral” pose and any non-neutral poses are defined by subtraction from this one so that the actual morphanimation can then be adequately calculated for any model based on its own, specific, “neutral” pose. While I haven’t yet reviewed this in enough detail, it would seem to me that a similar approach could perhaps be used for skeletal animations too – a neutral skeletal pose as reference for animations described as changes to *that* rather than as tweaks to a particular set and setting of bones.

For the next steps on this, I’ll have to figure out and somehow test practically the socket mechanism at the very least. Given however the desired subquest on graphics production, the socket mechanism by itself is certainly required but not sufficient – what I’ll have to do is to extract/decide on what makes the essence of “euloran”, fix that and provide acceptable (as in close enough to be natively recognizable but diverse enough to be also identified as distinct) ways to change/mix those. While some ideas on the topic are pushing around at various boundaries, the gritty part still has to start with digging deeper into the skeleton, meshes and morphanimations definitions, formats and use as provided by Cal3d since that’s the practical starting point currently – at the very least, I clearly have to figure out what exactly is required so as not to crash in segfault the whole lib whenever replacing a part with an “unexpected” mesh.

Given the above, I plan to continue with the focus on the characters part of the client data hierarchy unless it’s considered otherwise a higher priority to get the remaining missing parts back in first, even before sorting out some sort of way to cloth and diversify the characters.

  1. This role of the flags and their interplay with applied transformations otherwise is what tripped me over previously and had caused the dive-in of the model when moving about. Yet another puzzle solved, I suppose.[]
  2. CG is gruesome in more ways than one, yes. Still, I’m just telling you what is there, not like I made it *that* way, all right?[]
  3. Doesn’t this strike you already as such a deeply held conviction that knowing it *all* is always possible and even required and all that? I’m not sure how it can sit otherwise so apparently unchallenged right in code and on computers but there it is.[]
  4. The only thing I can say on this is that apparently I really am an incurable optimist, no idea why and how come. I can only shake my head in disbelief.[]

February 11, 2020

No Atmos in the Atmosphere (Eulora’s Defaults)

Filed under: Coding,Eulora — Diana Coman @ 6:25 pm

Having explored those past few days all the possible ways to get various aspects of air 1 showing up in Eulora’s client, I’m left with a working “air” element of sorts, a bunch of screenshots (scroll down for them) to show as illustrations of the whole exploration and otherwise a lingering question of little import to computer graphics: if it’s indeed a sphere but lacking any atmos, can it still be called an atmosphere?

Unlike the terrain and the water that I sorted out already, the “air” category doesn’t have just one single element since CS and the current client don’t bother with such lofty ideas as actually modeling anything. There are instead all sorts of bits and pieces that can be pieced together to make it seem like there is some sort of atmosphere: some sky, some clouds, some weather, some fancy potential effects of sorts and some ways for light to travel from the sun or any other source. So the rest of this article is a look at what I found out when taking each of those in turn and looking in practical detail at how and to what extent they can be made to work at all.

Light

To be able to see anything of the world at all, CS requires at the very least an amount of “ambiental” light (so light without a source as such, supposedly a sort of stand-in for the reflected light as it bounces off everything around) and a (some) source(s) of light overall. On one hand sources of light are more aptly items/structures in my hierarchy of objects and as such not really belonging here but on the other hand, the way the client works currently, there is the need for some “sources of light” that are simply environmental characteristics rather than items. So I went here with copying whatever such lights are defined in the current client: some ambiental light and 3 “suns” positioned through unknown methods in a way that “works” in the sense that you can see the stuff around and there is some apparent direction from where the sun shines or something. It’s a mystical light basically and it will be fixed as default in there, nothing more to do about it as far as I can currently see, since it’s certainly not the server’s job or interest to fiddle default lighting so things are visible given one rendering or another.

When talking of lights, it’s worth noting that the above fixed set of lights is not in fact the full story in the currently deployed client. The intensity and exact colour of light is made to depend on the time but at the moment this is not imported into the prototype client and I’m not fully sure it’s something worth importing as such: the way it’s done, there’s an xml file (art/lighting.xml) that lists *for each hour of the day* the values (r,g,b) of various lights (identified by their fixed name…) in different sectors (again, identified by fixed sector names). Some manager or another in the PS client code proceeds then to use those values to adjust each light periodically, to match the time of the day, interpolating between the values given for the current and next hour.

Besides the clunkiness and clumsiness of the above approach, there’s more trouble with importing it as found, with the most important problem being that the assumption of fixed sector names simply has no place at all in Eulora’s client. Moreover, there’s no good reason I can see for having this sort of hour by hour list in an xml if it’s basically a set of constants anyway and entirely client’s business too. So at the moment, it just remained outside entirely: in Eulora’s default client it’s always somewhere in the middle of the day, when things are quite visible. As a player I’m not even sure why the fuck would I actually *want* to have light dimming so I can’t see things properly but then again, it’s been already observed that I’m a sort of software radical, especially when dealing with graphics, so let me know if you have some reasons for which I should really spend some time to implement time-related changes to the overall light (and then possibly getting in a night sky too, I suppose, see below for the sky and clouds section).

For an illustration of the light conditions, here’s a screenshot from a “shaded” spot so you can notice the light and shade divide as it happens with the current set of ambiental light and three suns:
airdefaults_10_1024.png

Sky and Clouds

Looking at the currently deployed client in the usual places (art/world.zip/island.xml for the factory+mesh; then off to art/meshes.zip/sphere.xml for the definition of the factory; then art/materials.zip/materials.cslib for the definition of the relevant materials) reveals that the euloran sky is currently a generic mesh defined painstakingly through enumerations of vertices and corresponding texels (texture coordinates at each vertex), normals and colours (which are all set to 1 and probably of no use since the texture is added on top anyway but hey, it has to be there so it’s there) as well as a whole set of triangles to piece up whatever shape the whole thing ends up to be (possibly an approximation of a sphere if the name is to be trusted indeed but I haven’t really bothered to check). Such a brilliant way of describing the thing that there are – line numbers for the win! – about 2050 vertices and when you multiply that by 4 and add on top the list of triangles and a bit of xml tags too, the file ends up having a bit more than 8200 lines in total – just because it can!

While I could easily just use copy/paste to get the above horror as constants in the default code, I find the idea so abhorrent that it’s just about the last thing I’d do – if there really was no other practical way to get some darned sky showing properly at all. Happily though, there is a more reasonable alternative in this case: with a bit of fiddling of parameters, it turns out that CS’s code for generating programmatically a sphere can do the job quite fine: the fiddling was more a matter of figuring out the exact number of rim vertices that would fit the existing texture well rather than anything else. So instead of littering the code with those thousands of lines of constant numbers describing a sphere, it works sanely for once to simply call the code to *generate* the sphere a be done with it! I have no idea why exactly wasn’t this done to start with but I am not even all that curious to find out really – what difference will it make anyway?

While the sphere – programmatically generated or boringly xml-enumerated – provides the so-called geometry of the “sky”, the rendered image is as usual a texture. Aiming to figure out what options there are and how they work, I made a few attempts to see if the shaders provided as “sky” and “clouds2” are of any use – it turns out they aren’t, at least not as they stand undocumented and without wasting a lot of time to get to the bottom of everything so that at least ~something shows up working. From what I could tell and for the very little that this is worth currently, the clouds2 shader aims to programmatically animate the texture so that you get supposedly moving clouds. Nevertheless, the default client will *not* get moving but fixed clouds, as already neatly painted in the image file that serves as texture for the sky cloud. Anyway, as part of the testing, I have tried out a different “sky” texture that I found in the “art” directory of the current client and the result was a rather weird-looking sky-thing:
airdefaults_20_1024.png

Getting back to using the usual clouds texture works though and the sky looks reasonable again, even reflecting quite well in the water – along with Foxy’s long and slender-by-water-effect legs, of course:
airdefaults_19_1024.png
airdefaults_11_1024.png

Effects

Since one might reasonably want some pretty sparks or something around, I went ahead and implemented in code the sparkly sphere that is currently defined in xml and shown on the beach in the client 1.2. It is using a particle systems and it goes on to tediously set quite a lot of parameters for the effector especially but with the right shader applied as well, the result can actually turn out quite prettily indeed, with both the “snowflake” texture and the currently used “spark” texture:
airdefaults_7_1024.png
airdefaults_8_1024.png

Weather: Rain, Snow and Fog

Moving on to weather effects, there’s fog as the simpler part and then rain and snow that are just variations on putting together in some predefined manner some particle systems. As my previous attempt at sorting out the particle systems was rather frustrating and with dubious results, I started with them first. As it happens, my suspicion that the poor effects of last time were linked to not using a shader/the right shader incantation turned out to be correct. Combined with the fact that meanwhile I ate – if rather reluctantly – shaders for breakfast too, it didn’t take all that long to obtain some pretty snow for instance:
airdefaults_16_1024.png

The shader used by both snow and rain is “particles_basic” and one remaining wtf that I had to sort out – to the degree possible – before being able to obtain the pretty picture above was the meaning of “type of shader” as set in code and otherwise not really mentioned or listed anywhere. What I finally figured out is that there is a set of predefined types (which is however nowhere properly listed as such as far as I could tell and goddammit I did look in the code and in the manual and in the api and on the dark side of the moon too) and those in fact are linked to – but not named as, nooo – different rendering passes! So that you can use the right shader but if you use it with the wrong type aka in the incorrect rendering pass, you get bupkiss result and can go bang your head some more as there’s no way to tell exactly why. This is by the way the solution to last article’s puzzle re using the same shader 2 times only set with different “types” – it is indeed the same and will indeed be run twice only in different rendering passes so the effects accumulate/interact in whatever way they do it (in some cases there are even values passed between successive rendering passes for instance…). I suppose I should be happy I solved my own puzzle or something.

Leaving the above rendering passes mess aside for now, it further turned out at a closer look that this glorious particles_basic shader does precious little really to the point that the observed effect is simply one of …making it work well with textures that use black for “transparent” instead of having an alpha channel. Doesn’t this sound though like a truly mind-boggling reason to use a whole shader for? Because to me it does and to such extent that I couldn’t just let it be – so I took the silly snowflake texture file and made the equivalent image only with transparency properly set through the alpha channel. After which, I made it snow again only without any bloody shader in the mix and lo and behold, you tell me the difference:
airdefaults_15_1024.png

The even funnier part of the above great shader use though is what happens therefore if you do both aka you use the shader (that worked with texture having black stand for transparent) AND have transparency in the texture itself – it’s snowing translucent cubes!
airdefaults_17_1024.png

Both rain and snow are painstakingly set in code with all the emitters, effectors, material and shader(s) + shader variables, as quite specific to particle systems. The main difference is with respect to texture used (since a snowflake is meant to look different from a water drop) and otherwise the particle size range. The shader does exactly the same sort of not-much as for snow but I haven’t bothered to add transparency to the water drop texture too so here’s how the current texture looks with and without the shader applied:
airdefaults_13_1024.png
airdefaults_12_1024.png

Given that the main difference between rain and snow really is just a matter of texture, it’s probably possible to simply fix the particle size and a lot of other parameters for the “effectors” and “emitters” and whatnots and then just have one single default “rain” that can then rain down even frogs if that’s the texture given. However, it turns out that there’s some deep trouble afoot with the current implementation of particle systems in CS: first, they are so inefficient that extending the rained upon area to the whole island basically doesn’t quite work (there was an initial freeze while CS calculated whatever it calculated for it all and then it was still visibly suffering while pouring all that thick rain around); second, whenever rain and/or snow code is in, the exit ends up in a segfault – as this is many levels deep in CS, I haven’t really spent time to investigate it and find the exact culprit but it seems to be related possibly to the specific emitter that rain/snow use since the trouble does not manifest for instance for the “sparkly globe” effect (see the previous section above) that uses a different emitter.

The fog is simply a property of the sector as a whole – there is no “local” fog even possible, unless you make perhaps your own as a particle systems I suppose (could have fog-particles perhaps?). One can set the “density” and the colour of the fog (r,g,b) – basically the effect is similar to putting on a pair of glasses tinted with the chosen fog’s colour and having thicker or thinner lenses depending on density. There is also a further “mode” parameter that chooses -as far as I can tell- the way in which exact fog values are calculated in the distance with the available “models” being called CS_FOG_MODE_NONE, CS_FOG_MODE_CRYSTALSPACE, CS_FOG_MODE_LINEAR_CRYSTALSPACE, CS_FOG_MODE_EXP, CS_FOG_MODE_EXP2. The only one I could find used at all seems to be CS_FOG_MODE_CRYSTALSPACE but at any rate, I made some experiments with others and there is either little visible difference or the whole thing looks even worse than it does otherwise. Moreover, I can’t say I see exactly any reason why I’d want fog in my client as a player but do let me know if you still think it should be in there – and in what form exactly. To get some idea of how it looks like, here are some pics of various “fog” experiments:

The bluish fog (r=0, g=0.1, b=0.1, density=0.02):
airdefaults_2_1024.png

At one extreme, the fog so white that it…shines in your eyes:
airdefaults_14_1024.png

At the other extreme, the fog so dark that everything turns into a black and white disco style:
airdefaults_1_1024.png

Still shiny fog but toned down a bit:
airdefaults_4_1024.png

When fog is more like darkness than like fog:
airdefaults_3_1024.png

Possibly almost reasonable – if rather graying – fog:
airdefaults_9_1024.png

Conclusion and Next Step

Given all the above, I’m overall satisfied that there is now some working default “air” for Eulora’s sectors even though no depiction of weather or of day/night changes. The shiny globe effect seems to work surprisingly well so far so I’ll keep it still on the list and possibly have another look at it after sorting out the items and characters to see if/how it can be attached to an item or to a moving character, if that is something of interest.

The next step for now is the potentially more time-consuming and iffier sorting out of a working default for characters with animations and all that included so there will likely be a longer wait until the next article in this series.

  1. No, not various airs and not even various arias – though these might be in waiting when I get to look at sounds.[]

February 6, 2020

Eulora’s Default Water without Water

Filed under: Coding,Eulora — Diana Coman @ 3:39 pm

Having set Eulora’s soil in stone (and grass and sand), I turned my attention this week to the waters that should naturally be found around an island – if it’s meant to be any sort of island at all. You know, those deep-deep euloran waters that hold all sorts of resources and unexpected benefits.

Last time I looked at water generation, I considered it had to be nothing other than some specific use of the procedural texture that CS supposedly provides for water rendering. A closer look at the corresponding CS plugin code 1 revealed however that it’s yet another very specialised thing to the extent that it comes with methods for making “puddles” at a given point – supposedly what the surface should look like if you throw something into the water. At the core of it though, the whole “procedural texture” shtick is an animated texture aka a set of direct writings/changes to the render buffers using the engine’s “ticks” as time units to play the desired sequence. Given this rather disappointing find, I don’t quite see any strong reason to import into SMG’s protocol those “procedural textures” as they are currently in CS, they seem to be utterly focused on client side rendering again and as such entirely client’s concern and choice.

Having set to rest my hopes of procedural water generation, I turned to the existing client’s pile of .xml files to extract from there just what and how it defines this water thing anyway, since the whole idea is to use whatever is currently in use, right? So in I went and here’s what I gathered:

  1. From island xml file (in art/world.zip), I found out that the “water” is but a mesh like any other so an “item/structure” in my hierarchy of data, only using a custom factory called “water”.
  2. Following the factory clue, from the water 2 xml file (in art/meshes.zip) I found out that the whole of Eulora’s water is in fact but a 2D rectangle, a sheet of blue if you will, cutting through the whole world to show only where there’s nothing else with higher rendering priority! And moreover, the whole thing is simply defined in the factory through its vertices, normals, texels (texture coordinates) and triangles – the minimal thing overall for a rectangle so that’s 4 vertices (with corresponding normals and texels) plus 2 triangles. The whole illusion therefore has to be achieved through the thin veneer 3 – aka material used – on top of this rectangle.
  3. In search of the material therefore, it was off to art/materials.zip and the materials.cslib file within. A look inside of it revealed that the material “water_02” (as jotted down from the factory’s definition) is a thing with 2 shaders (set as different types but using the same… shader; does this make any sense to you?) and 6 shader variables of which one is a texture but not the base texture but the normal texture (aka it’s used for more precise definition of normals across the surface rather than as painted skin to spread upon the surface).
  4. The “normal texture” from above turned out to be indeed defined simply in the same materials.cslib as a file that even exists where one would expect it to be (in art/materials.zip) except… its extension is missing, its type is actually unknown (it’s not .ani as one might guess from the name, it’s not .png, nor .jpg, nor .dds for sure) and the current client in fact complains loudly at this, if anyone ever paid attention: it can’t load that “generic_water_001_ani” thing no matter what. So…uhm, ditch it then and use some reasonably named “w_normalmap” since it is meant to be a map of normals after all.
  5. As for the shader with multiple uses, a look at it indicated that it’s indeed the part doing in fact all the heavy water lifting so it got added to the list of defaults that the client loads.

With all the above set, the rest was simply a matter of writing the code to create then the default water precisely as defined in the current xml, only using CS directly from code and without requiring any xml at all. There were a few funny steps on the way including the water with error texture when I had to test that ani file just in case it could be loaded somehow:
waters_1_1024.png

Then the part where Foxy walked on water because the xml had this tag “colldet” that you might be forgiven to think it means adding collision detection as silly as that may sound when you render water – of course it does not mean such a simple thing; what it means is that you should *toggle* collision detection on if it’s off and off if it’s on and of course you just…know what it is anyway, right? It’s also with a different water “normals” texture that I tried for testing purposes and it doesn’t even look all *that* bad although the clear repetition is rather grating to my eyes:
waters_4_1024.png

And some funny legs reflections that are for now still in there, mainly because apparently that’s how Foxy’s current shape ends up reflected currently:
waters_2_1024.png

Finally, here’s also an even reasonable shot of Foxy getting out of the water on to the Euloran harsh and unforgiving sand:
waters_3_1024.png

An interesting thing to note with this water-plane approach is the fact that it’s currently… in the way of light at times so it introduces weird “dark” effects in places on the island. For the time being I’m not too bothered about it since it might anyway end up sorted when I look into default lights and moreover, it’s again both a client-side concern only and the exact way in which the existing client works anyway. This being said, I still think this whole water-plane thing is silly – especially given that once you get inside the water fully, there’s of course just… air aka “no water inside the water”. Ain’t this just a great illustration of how modern stuff turns out to be at a closer look, various sorts of no water inside water, right?

  1. The main bit is in cs/plugins/proctex/standard/prwater.cpp if you are curious about it.[]
  2. There are actually TWO files in there, namely water and water2 but as it seems to be the usual case with the existing client, one is just not used currently at all.[]
  3. At which point my mind sniggered into song, of course, as this must clearly be what most people mean all along with all the heartfelt “river deeeeeeep and mountain hiiiiigh”; it fits this sort of water and this sort of deep, heh.[]

January 31, 2020

Eulora’s Default Island: Sand, Grass, Rocks and Nothing Else

Filed under: Coding,Eulora — Diana Coman @ 4:36 pm

Less than 2 weeks after I’ve set Eulora’s client down, the resolution came of course to… pick it back up! Nothing like a bit of up-down to hm, build muscle? Anyways, there’s nothing lost for the exercise and quite a lot gained really since I got some time to unload more than just the code 1 and the resulting discussion sets a clearer way to move further:

mircea_popescu: anyways, to complete the frame here : http://trilema.com/2020/get-back-to-me/ eventually was resolved through discussion here, specifically : 1. that the expectation we’ll have to produce a data model constituent part of the original dilemma was in fact produced by 2. an unstated assumption that we’ll resolve the reuse-or-rewrite dilemma in the sense of re-write, 3. driven in turn by the felt pain of dealing with the damned thing as it exists.
ossabot: Logged on 2020-01-27 11:26:47 diana_coman: the way I see it, there are 2 main ways to get the gfx back in: a. use what already works, hence client 1.2b b. implement own aka go from the existing prototypes in 2.0 to production stuff so yes, including non-stiff etc
mircea_popescu: but the correct decision is to reuse, on the grounds that however painful we might be aware it actually is, in the most organic sense of awareness, nevertheless the alternative is actually specifically the sort of thing we’re trying to avoid.
mircea_popescu: as in, the ever-circling avoidant behaviour of great recent fame, that’ll never complete nor does ever produce anything else but enlargements of scope, in a reminiscent threadmill.
ossabot: Logged on 2020-01-10 08:13:57 mircea_popescu: with the alt-pantsuit conceptual threadmill, the “counter”-cultural, by-and-for-goffy-kids yet JUST AS PANTSUIT (in the only important sense) threadmill, where nothing is replaced with ever “larger” nothing on a dilated schedule.
[..]
diana_coman: mircea_popescu: specifically and for full clarity though: what’s desired next step currently?
mircea_popescu: either a working 2.0 with 1.2 gfx stack or a detaled failure report.

Given the above desired next step combined with this situation of having set down the code for a few days at least, I took first some time to look in parallel at the currently deployed client (1.2) and the prototype client (2.0), as I picked essentially both of them up again.

The result of all this looking was some barf of course but more usefully an initial map as to what needs to be done. And given the goal to get the gfx stack fully back in, I focused first on cementing some “default” loaders for each *type* of graphics asset that is going to be of any use: terrain, water, air, character (cal3d), item/structure (CS mesh), sound, icon, skin. Those defaults will be as minimal as possible and will serve as placeholders whenever needed – if an asset is missing or the client doesn’t know about it because reasons 2, then the client will use the default for that type and be done with it. Sure, everything will look the same and/or confusing if there’s too much of this going on but if you don’t like that, you are warmly invited to get off your ass and make a better client already, there’s your motivation for it. You’re welcome.

The last attempt at getting to the bottom of terrain creation with CS ended up with a very mirror-like terrain that is really not quite good enough since one can hardly see much with all that reflection. So I’ve spent the past few days going again through it all, mapping out all the various bits and pieces from xml files everywhere to CS’s terrain2 plugin and interfaces and whatnots.

To start with, since it’s defaults and pragmatism all around this time, I’ve set the prototype client to just load the main shaders anyway since the rest of the code can’t really do anything remotely reasonable without them. Let them be there, part and parcel of “the client” and none of server’s concern otherwise. This bit already was funny since it turns out that there is one list of shaders where you’d perhaps expect it, namely in data/shader so the usual “inventory xml” file: shaderlist.xml. Except, this list is very small compared to the full set of shaders that ship with both CS and legacy client. So what gives? Well, it turns out that there’s further listing of shaders from within the…materials’ inventory! Now add to this the fact that each shader may reference snippets and other shaders that are perhaps not in the list and you might get some idea as to where this leads if one follows it blindly.

By now though I have no intention to follow anything blindly and so I don’t care about its references, I’m not reimporting PS’s full-blown dependency-dragging loader either 3 and I’ll use instead CS’s basic loader to do the best it can. So I’ve set it explicitly and yes, hardcodedly, to load a list of shaders as I extracted it out of the current “shaderlist” and “materials.cslib” leaving it to handle or not the rest, who cares all that much. Surprisingly enough – or maybe not – it turns out it even works so I marked this part as done and moved on to making a default “Terrain” that will be used for any sector if need be.

While the naive approach with respect to terrain would consider the heightmap to be everything that’s needed, the reality is of course rather messier than that. The legacy client does in fact several dances back and forth on this, to the point where the same files are in two places and the same mesh is defined in parts in the world xml file – I have no idea why really, there’s no requirement for that and it certainly makes the whole thing rather confusing to follow.

Anyways, turning the terrain2 plugin inside out this time, I got at least to figure out the crucial bits: yes, the heightmap is needed; so is the base material in fact, despite what one might think at first – it’s really this base material that does the first and most crucial layer of work. Setting this base material as it is in the legacy client requires the basic texture of the terrain, its lightmap, materialmap and normalmap as well as the three basic materials (texture+normals for each of them) that are to be used throughout.

The less than ideal news is therefore that “defaults” in this case end up being 11 .png and .dds files rather than just the one heightmap.png. The good news is that I figured out the idiotic shader and shader vars setting for this particularly convoluted thing 4 and the result manages to actually look -perhaps unexpectedly- like some terrain after all:
client20_terrain_1_1024.png
client20_terrain_2_1024.png

If you don’t quite like the rather rough look of the terrain above, note that it’s indeed a very basic default terrain. The legacy client adds to the above several passes of rather intricate specifications of generating 3d grass/sand/rock of sorts with windspeed and density maps (aka yet another set of .png + .dds files that would need to get added to the defaults dir). At this stage I’m not even really sure that this side of things is really much concern for S.MG and the server – let the client prettify any terrain as it sees fit, after all.

For future reference, I’ll note here that the whole dance with a “material palette” from which supposedly the terrain plugin selects and blends the materials based on the height of each point turned out in practice to make very little difference by itself. And so while I wrote the code to do that too, I’m not even sure it will make it to the production version as anything other than a note in the comments. This “palette” is such a pretense: there are at all times precisely 3 materials and nothing more, since they are otherwise specified via yet another .png and taken as the red, green and blue values respectively, so no, no space for more than 3 anyway. The only thing that may change at all are the 3 materials themselves.

For now I’m happy with the above default terrain, as rough as it might look. Next week I’ll move on to setting in stone the rest of the defaults and see what cans of worms await to be squashed there.

  1. Though this very same purposeful and focused unloading *also* highlighted just how much I’m carrying otherwise at all times – still am, yes, and looking back have been for longer than I have online references to point at.[]
  2. Those have to do mainly with that idiotic expectation of knowing everything upfront.[]
  3. This would be the one reading the xml and all that shit.[]
  4. Because it’s a base material set on the default cell of the factory for terrain, the route is not exactly the very same as in other parts. Well, it wouldn’t be all that fun otherwise, would it.[]

January 21, 2020

De Facto Data Somethings in Eulora’s Client

Filed under: Coding,Eulora — Diana Coman @ 7:21 pm

There was a rather important question asked only recently in #eulora:

mircea_popescu: inasmuch as we do have a ~de facto~ data model, the gap must be bridged. “why aren’t you using what you are already using again ?”

To answer the above, I went and extracted what and how can be found currently in the full client and its dependencies code, to have it all clearly in view for once. The report of that investigation is below in quite a lot of detail but that detail means also about 5k words in total, so here’s directly the conclusion of it all namely the answer I can now give to that important question above:

  1. The de facto data model is not really one data model at all but overall as it stands more of a confused mix of several formats, approaches and accidental structures of all sorts, all wrapped up and intertwined together into one single large pile of code with fundamental assumptions that are the exact opposite 1 of those of S.MG and that requires a lot of effort for the smallest adaptation to new/different requirements, working otherwise mainly for as long as it is used precisely as it stands.
  2. The thick layer of PS on top of the comparatively saner CS and Cal3D core makes access to and use of CS/Cal3D data models directly especially difficult/effort intensive because everything is built quite on purpose precisely to avoid such use. Moreover, the overall de facto data model such as it is has as fundamental assumption that *all* assets are known upfront with no new knowledge/assets ever added during runtime. Consequently, there is a significant tension between the de facto model and the desired model since the focus of ~everything in the current client is on handling local loading of potentially large but fully known upfront lists of assets while the desired model is instead meant for transmission and/or discovery and loading of new assets as an ongoing concern as the code runs.
  3. At the core of the current client (hence in CS and Cal3D essentially) there are some clearly defined data models and formats, most notably Cal3D’s file formats and CS’s internal data models (that pack however shaders and shader variables into materials as standard) as well as some basic file formats used otherwise such as .png, .dds, .ogg or .wav. However, none of those different sets cover *everything* that is needed, meaning that they can only provide perhaps a *part* of that desired definitive universal data model. The rest can be – it would seem to me as a result of the detailed investigation below – extracted/synthesised out of what CS allows but using it as a universal data model is problematic with the existing client because of the PS layer on top that would require significant effort to turn around from hiding/making direct access difficult to using it directly when and where desired.
  4. Making the best of it all, here’s a proposed list with the parts for which there is at least some clear format already (if not necessarily easy to use in the desired manner with the existing client such as it stands otherwise) and the parts on which a format would need to be decided on/extracted out of various options, fallbacks and CS’s own API even:
    1. Characters (animated mesh objects): Cal3D’s formats, ideally the binary set (as they are more compact); this means: .csf (skeleton), .caf (skeleton-based animation), .cpf (morphanimation), .cmf (mesh), .crf (material) + raster images for the textures that are referenced by filename from .crf; the preference for the texture format would be .dds files with size as power of 2 as CS otherwise performs the calculations needed to bring anything to the equivalent of that anyway; alternatively, CS compiled with the usual set of plugins supports a variety of usual image formats including .png, .jpg, .gif.
    2. Items/Structures (non-animated mesh objects): the options here would be to either force the use of the same .cmf from Cal3D’s set above (the trouble with this being that it assumes the presence of a skeleton/bones, which is not going to fit well with items/structures and moreover it will require some conversion for use with the current client even beyond the PS layer, namely to fit the internal CS non-Cal3D mesh) or otherwise extract and define a format based on CS’s internal representation/specification of a mesh (this is essentially a plain and long enummeration of all vertices, triangles that make up the surfaces and submeshes if any).
    3. Terrain: there is no clear format for this; the de facto “model” relying on CS’s Terrain2 plugin seems to require simply a bunch of image files for heightmap, material palette, base material and alpha as a minimum, using at all times some of CS’s predefined shaders for terrain. PS reads/expects those packaged via an .xml file that matches image files to their role for the terrain so either such .xml specification is imported as part of the format or that part of the client (the loader essentially) has to be adapted/changed.
    4. Particle systems: there is no format or specific model currently available; in principle it could be extracted based on the concrete set of parameters required by CS’s way of specifying existing particle systems (hence, a specification of emitter and effector parameters) but it’s unclear just how powerful such a model would really be as there are only a few types of particle systems implemented in CS and each of them tends to have its own specific parameters too.
    5. Light sources: there is no format or specific model currently available; extracting it from CS’s internal representation provides at least a relatively simple set of characteristics so this could be perhaps done as such.
    6. Ambiental light and fog/air effects: there is no format or specific model currently available; de facto, ambiental light values as well as fog and air effects (mostly weather-related) are currently scattered through several .xml files that are mainly PS’s: some values for ambiental and fog are given in the world.xml file (art/world.zip) while hour by hour values for ambiental and rain are given in a different .xml file for a full day and the client expects them as such. The more reasonable approach here is to simply keep those as sets of rgb(a) values rather than force-define anything more complex than that.
    7. Effects: there is no format or specific model currently available; CS does not treat this as a separate category as such; PS on the other hand has a de facto .xml format for defining effects attached to items but there is no clear specification for it and existing samples focus mainly on defining essentially animations of sorts. Perhaps this can be skipped entirely as nothing different from particle systems and/or sound and/or some specific animation defined through the corresponding formats.
    8. Materials: .crf (Cal3D’s format); this will require though some conversion to use with the current client even beyond the PS layer, since it is not a direct fit to CS’s internal data model for materials. Moreover, using this format will fail to take advantage of some of CS’s techniques for materials such as using animated textures (literally changing a set of textures in the render buffer so the result is an animated surface). Arguably such effects though will have to be obtained by other means if desired by any specific client.
    9. Textures: raster images with sizes as power of 2, preferably .dds format but possibly supporting a wider set including png, tga, bmp, jpg, dds, gif, mng, jng. NB: there is no provision or format available for any procedural texture generation in this case (CS provides procedural texture generation implementations for water, fire, plasma and dots).
    10. Icons/2D/GUI decorations: same as textures above. Basically any image can be used as either icon or texture at any time anyway. NB: there is no provision currently for using any vectorial representation directly.
    11. Sound: .ogg and .wav files.
    12. GUI/Skin: this is currently specific to PS and as such perhaps the “model” can simply be a .zip file that contains whatever files and everything else a specific client requires to skin its GUI entirely. This would be the only option I see to provide this too along the rest without making it either dependent on the current client implementation (hence PAWS in particular) as such or otherwise truly the protocol/server’s concern since it’s strictly a matter for the client to decide on, in my view.

Overall the parts and formats listed above focus on the assets themselves and entirely avoid the specification of function and structure that is currently de facto done via .xml files. The following section provides a more detailed look at how things happen with the currently deployed client.


Current Client Situation

While the de facto data formats in Eulora have been investigated previously and in quite some detail too, the focus was at the time on understanding the whole thing well enough to be able to extract out of it the useful and – hopefully – discard all the useless and the worse-than-useless currently bundled up with it. The attempt failed though, despite the progress made in that vein. So here I am taking stock of the various parts of the swamps I am stuck with, for lack of having anything better – meaning something that actually fully works – to take stock of, how lovely 2. Anyways, them being the swamps is no excuse for not having otherwise a clear picture of what’s in there, so I’ll aim to bring the clarity even to an inventory of mud, what else is there to do anyway.

To start with and for an initial top-level, orienting view, note that there are effectively at least 3 (three!) layers of “formats” weaved and intertwined in the current client, from bottom up:

  1. Formats supported by the various external libs and plugins that CS 3 uses. Those are usually standard formats such as .png and .jpg for images for instance, .ogg for sound or .caf (Cal3D’s format) for animations. Note that the full list of those is not really a fixed thing since it *depends* on what components you have on your system and include/compile with the whole client! Moreover, *none* of the different plugins provides a single set of formats that would cover *fully* all assets or even all graphical assets so one can’t just pick from there a single set and be done with it.
  2. Formats defined/imposed by CS. Those can be further split into internal and external (to the engine code as such). The internal formats are most relevant for textures, materials and meshes since they fix a certain data model and requirements for defining and working with those (see below for the more detailed descriptions). The external formats are based on xml but with specific tags, restrictions/rules and parsers for things such as “shaders” or scene/world/object definition in standalone – and at times or to some extent hardcoded even – files that CS knows how to work with.
  3. Formats defined/imposed by PS 4. Those are yet another set of xml-based “formats” 5, this time aiming – as far as anyone can tell by looking at the gnarly result – to provide the way to list and describe existing assets of the game, from 2D icons to 3D meshes and GUI widgets (windows in game).

Given that the original goal was to arrive at a clear definition of a definitive 6 universal data model, I’ll focus on the “clear” and therefore use the above three neat layers to structure the whole discussion of the de facto formats and emerging “model”. The alternative would be to follow closely and describe exactly just “how the client currently works” but that results quickly 7 in a quite predictable muddle (given the de facto muddle it reflects), so not much use for clarifying anything or for answering any question. To state it plainly though: I’m aiming to structure the text for clarity but not by glossing over any gross details – my aim is to mention such muddy details where they are relevant but to not let them drive my text even if they do get to drive the legacy code in quite a few places.

Before diving into the concrete details and exact formats currently in use on client side for all sorts, it’s worth noting a few fundamental characteristics of CS that drive quite a lot of the whole de facto model such as it is:

  1. The focus is on supporting – somehow and to a best effort level essentially – a generic and not that well defined everything. The main implication of this is lack of clarity and added complexity of all sorts: there isn’t a clear single data format and model as such but at most a generic structure (most notably at the way materials are defined and implemented to include specific reliance on predefined shader types and variables) that still ends up forcing some choices, only not fully supporting them either (since that would break the appearance of supporting everything); there are always rather intricate fallback and alternative mechanisms at all levels from type of rendering (software vs hardware and then depending on what operations are supported by different types of hardware) to high-level object or scene definition (e.g. from code or through xml).
  2. XML is used (and pushed) extensively for a double – if never explicitly stated as such – purpose:
    1. to describe the structure and organisation of sets of assets on disk (all sorts, including but not limited to graphics) for a CS application. Sets of graphics/sound/UI assets are called “library” in CS parlance, for all their xml-only definition.
    2. to script from outside the CS application all sorts, from rendering steps (in definitions of shaders) to scenes, objects and even actions in the world (in definitions of sectors, meshes, triggers, sequences and portals). Especially for shaders, this gets quite intricate, it’s a sort of ad-hoc xml-based programming language, no 2 ways around it.

    Note that the scripting purpose of xml above goes well beyond mere description of structure and organisation or even statement of the purpose/use of things in there. To try and give an idea of this, the xml code at CS level is quite overgrown and does a lot of things, including but not limited to customising existing objects, describing how to create new objects out of existing assets, setting out what and where things are to be found in the world, calling and otherwise using (therefore fixing as requirements) specific plugins or parts of CS, defining new rendering sequences or even buffer contents and prescribing steps and parameters of all sorts for them.

    The saving grace of all the above is that most – though unfortunately not ALL – of it can still be done directly from code as well, without a lot of trouble. So in this sense, the xml reliance pushed by CS is more of an option than a mandatory part to deal with. Nevertheless, there is one significant part of this xml reliance that is deeply embedded and does not really have a clear code-only/no-xml alternative, making it more of a mandatory part than an option: the shaders which are fully described *only* through a spaghetti of xml files that rely on parts and “snippets” from one another, provide fallback or alternative to one another and otherwise end up hardcoded as part of the very definition of a material and at times of crucial plugins (the terrain generator for instance) in the CS engine.

  3. All identifiers (including those of assets loaded/created in the engine) are strings – combined with CPP’s own specific brand of string-problems (most notably the null terminator that may or may not exist and the allocate/deallocate memory dance), this is a pain. The approach of CS/PS to avoid the pain is by having everything rely on CS’s implementation of csString as a utility that takes care of the gritty details of using strings in CPP. Furthermore, there are several hashmaps used to map those cumbersome string ids to saner numerical values but those of the engine itself are not directly exposed. Adding to this, CS provides a way to “attach” data to any object so one could in principle add a numerical id but there is little gain from that since all searches for objects still rely on the string “id”.
  4. There is an implicit assumption that all assets and the game’s world are fully known and defined upfront as well as outside the code itself. The programmatic option – as much as it exists – ends up overall underdeveloped in comparison to the xml support for the fixed, outside of code definition of everything.

The above fundamental characteristics of CS get pushed even further at the PS level on top: the fallbacks and “support everything but don’t commit to anything in specific” are pushed even on to graphics preferences and configuration files (of which there are several types and several layers); the xml gets added functionality as means to describe appearance of widgets (windows) in the client’s interface while still relying on even more hardcoded paths/names both in the xml itself (which references resources as it expects them to exist) and in the code (which looks for various xml files by name and path (if at least the path is relative to some directory as defined in the VFS – virtual file system – configuration); the expected full upfront knowledge of everything that may exist in the world gets cemented by PS: there is virtually no programmatic creation of anything and instead, everything is both supposed to be described in external xml files (+ referenced graphics/sound/effects files) and otherwise known and loaded before the game can even start at all. Essentially the PS client chooses at most what to show to the human player at one time or another out of its otherwise full knowledge of what’s possible and what not 8. Things can get at most cloned or deleted, hence the player may encounter “in the game” something that seems “new” to them only if they really didn’t look in the client’s directory as otherwise everything’s already there as long as it has some sort of asset-based representation at all.

Leaving aside for now the above characteristics of CS and their various implications, here’s otherwise the structure such as it is of the de facto data model at the core of the engine itself, from top down:

  • the whole world is made of “sectors” that may be split into “regions” and contain any number of “sources of light”, “effects” and “mesh objects”;
  • sectors are connected to one another through “portals” (that are really just a special type of mesh objects);
  • sources of light do not have geometry and are defined instead through a set of functional characteristics, they are basically ideal lights that can be tweaked to some extent (see below for full list of characteristics);
  • effects include particle systems as well as sequences (a set of operations tagged with relative time so that they get executed in a sequence);
  • mesh objects can contain in turn other mesh objects (in a hierarchical structure) and are defined ultimately through their geometry (aka vertices and triangles) combined with assigned materials that describe the surface appearance and, where relevant, skeleton-based animation (CS supports animation through either its own sprite class or the external Cal3D lib);
  • materials are in turn made out of one or several textures combined with any number of shaders and parametrised through specific values assigned to variables from a predefined set;
  • shaders are defined via xml only and contain instructions for the rendering of the material data, introducing further their own variables, parameters and dependencies on other shaders;
  • textures are the most basic element and they are either directly a colour represented through the three components, red, green and blue or otherwise an image file that is meant to be of a power of 2 in size (with the exact list of supported formats depending on which plugins are linked with CS on any specific install). There is also a limited selection of procedural approaches that can be in principle applied to any texture, namely for fire, water, dots and plasma appearance. Confusingly though, CS still wraps all textures into a default “material” so despite loading textures, it’s always materials one gets to work with from the code. Moreover, further tweaks such as animating textures are done through render buffers and as such essentially through shader code, whether it gets written as a separate xml-based shader specification or otherwise implemented directly in the code.

Adding more concrete details to the above overall description:

1. Sector – this is an abstract type (it does NOT define by itself any geometry and as such it does not have dimensions or position or anything of the sort) that works as a way of grouping together a set of assets for rendering purpose (overall rendering characteristics are defined at this top level, including ambiental light characteristics, some air effects such as fog, overall sources of light if any and specific choices of visibility culler and collision detection system). Everything that is shown/heard as part of the game has to be contained in a sector and at any given time there is only one sector that is fully visible. Characters may be in more than one sector at a time but only temporary and as part of the way in which crossings happen from one sector to another, namely through “portals”. At CS level, everything in a sector and all properties of a sector can be changed in principle on the fly from the code although some changes may incur a significant computational cost (e.g. changes to dynamic lights that should trigger a recalculation of lightmaps).

While the above is given by CS, the current client PS implementation adds its own layer of complexity on top: the current code requires that all possible sectors are already defined in an xml file that it expects to find in /art/world.zip (this path may be changed but not on the fly, only from one of the many configuration files). Moreover, it also expects that it knows everything upfront about all sectors and that all sectors reference only assets/items/anythings that it already knows about (from other hardcoded xml files that serve basically as inventories and/or definitions of different types of assets). Finally, there’s the added hardcoded dependency of some of the GUI on some sectors and graphical assets (most notably the requirement for the “podium” sector that is used to display the character’s smaller version in the inventory window and/or at the initial character selection). Note also that PS’s current client implementation makes it very hard to actually change anything on the fly from the code since it quite specifically assumes that things do not change – they may be copied or otherwise not shown on screen or even not fully loaded into memory at one time or another but otherwise not really changing much.

1.1 Region – this is yet another abstract type, basically providing a way to split a sector into smaller chunks of contained objects (of all sorts, so not only physical objects) if desired; the main reason for this to exist at all seems to be the fact that the assumption of knowing everything upfront can result otherwise in “too much to load at a time” and this got solved by allowing the split of sectors into regions 9.

Here PS adds a whole layer that keeps track of various regions and “zones”, what should be fully loaded for each and even what specific picture to show the user while the loading is crawling at its own pace. It also handles its own added complexity from the current interaction with the server as some entities may come before their sector/region is fully loaded and so the client has a special sector created as a temporary purgatory for those entities, checking continuously to see when they can finally be moved to some loaded sector/region.

1.2 Light – there is “ambiental light” as an overal property of each Sector, as well as “sources of light” as abstract objects (no geometry) that have nevertheless a position from which the light is considered to be emitted. Ambiental light is defined only through its colour (rgb). Sources of light can be of 3 types: static (do not move and do not change intensity or colour), pseudo-dynamic (do not move but can change intensity or colour), dynamic (can both move and chnage intensity or colour). Each source of light is defined at a minimum through its type (static, pseudo-dynamic or dynamic), position (3 float values), radius (float value) and “diffuse” colour (rgb) that influences as well the intensity of the light coming from this source. Optional characteristics include a second colour (“specular”, rgb), an attenuation mode (none, linear, inverse, realistic, constant linear quadratic) and parameters (3 floats), a cutoff distance (float value) representing the maximum distance at which this light is considered to shine, a halo (flare, nova or cross, with corresponding parameters), falloff inner and outer angles for a spot light (2 float values).

1.3 Fog – this is an overall property of each Sector and is defined through colour (rgb), mode (none, linear, exponential, exponential2, crystalspace), density (float) and fading start/end distance (2 float values).

1.4 Effects – this is my own category really since CS doesn’t bother as such to fully group everything. CS provides a set of particle systems that can be programmatically used. As I discussed those in more detail elsewhere, I won’t repeat that part here. However, it’s worth noting that the current implementation of those particle systems relies on the predefined set of shaders that CS comes with and so they are essentially customisable to some extent through the code rather than fully defined (unless one adds a redefinition of expected shaders too, I suppose).

The current client’s take on effects is again – unsurprisingly – quite entirely reliant on various xml files. There is a hardcoded path art/effects that includes a bunch of .dds files mainly as well as a art/music/effects that includes a bunch of .ogg and .wav files. The paths to those are hardcoded throughout the client where the legacy code “knew” effects should be loaded from. The loaders generally look for an xml file that provides the inventory and tends to attach “resource names” to the physical files. At times, the xml file will also provide items to which effects are “linked” and the legacy PS code keeps effects in an array with references to the intended items. Even this de facto “model” is not all that respected really since there is at least one place where the “rain effect” is simply directly hardcoded as a texture to be loaded from the hardcoded path to the .dds file (see src/client/weather.cpp: const char* rainTex = psengine->GetConfig()->GetStr(“PlaneShift.Effects.Rain.Texture”, “/this/art/effects/raindrop.dds”);).

In addition to the above, PS also adds its own plugin for loading effects defined in xml files as illustrated in data/effects. The full format for those is not clearly defined anywhere and currently I haven’t fully investigated it either as it’s not directly in use. At any rate, the .eff xml files defining the effects contain also the definition of needed textures and materials based on the .dds files included next to the xml. The rest of the .eff file seems to define an object type to which the effect is to be attached and otherwise keyframes so essentially yet another sort of animation format done ad-hoc in xml.

1.5 Mesh Objects – from CS’s point of view, anything that has geometry at all is a mesh object. The main categories of mesh objects that I can discern are terrain, characters (moving) and items/structures (non-moving). For each of those, CS allows in principle any concrete implementation and inner data model to be used, as long as the corresponding SCF 10 interface is provided. In practice though, the existing client uses the Terrain2 plugin for terrain, the Cal3D plugin for characters and CS’s native basic mesh for items/structures. Note however that all of those are wrapped up in a few layers of PS obfuscation, most notably through the GEMObject hierarchy and psCelClient + psEntity classes. The GEMObject hierarchy is a core element of the current client too and as such it’s not trivial to either change or remove. Getting directly and using CS’s interface from the existing client is not straightforward at all because the PS layer works precisely and purposefully to insulate away from touching CS directly. Nothing is impossible, of course, but whatever clarity there is at CS level needs to be considered through the murky and intertwined layers of PS on top as things currently stand.

1.5.1 Terrain (from Terrain2 plugin) – at CS level, this requires as a minimum a heightmap file, a material map file and a material palette (aka several full material definitions including their texture(s), shader(s) and shader variable(s)). The CS Terrain2 plugin is directly linked to some specific shaders that handle the splatting and seem to further require – for better appearance – at least an alphamap of the whole terrain. The current PS implementation relies on XML to define the terrain too and expects as usual that everything – including all possible terrains and maps and even the pattern of sunlight throughout the various hours of the day! – is known and fully loadable upfront.

1.5.2 Characters (from Cal3d plugin) – when the Cal3d plugin is used, there is an XML description required (mainly by the PS layer since CS can otherwise simply do it from the code directly) that specifies the relevant Cal3D files for any given character. Those Cal3D files come in 5 main formats (the formats mentioned below are binary formats; they all have equivalent xml formats as well – those are directly readable but otherwise more bulky, of course):

  1. Skeleton file, .csf – this lists the hierarchy of bones that make up the character’s skeleton. Each character is supposed to have at least one bone.
  2. Animation file, .caf – this defines the pose of the character at each keyframe (the animation will interpolate the pose between those keyframes). Keyframes are further grouped by tracks with one track per animated bone and have to include the time, the relative position and the relative rotation with respect to the parent bone (if .caf file).
  3. Meshanimation file, .cpf – this is meant for changes in expression for instance or more generally animation of meshes/submeshes without relying on /requiring bone movement as such. Note that this format is mentioned in the Cal3D docs, but it is not currently in use at all and it’s unclear how well it is actually even supported otherwise (to establish this, I’d need to investigate in more detail both Cal3D itself and the CS plugin for Cal3D).
  4. Mesh file, .cmf – this describes in detail the mesh as a hierarchy of submeshes and with references to materials in use as well as the weighted influences of the bones on each vertex. This would also include morphtargets if the .cpf format is used for additional animation(s).
  5. Material file, .crf – this describes a material through its ambient, diffuse and specular colours (each given through 4 values for red, green, blue and alpha respectively), its shininess (a float) and a number of textures that have String identifiers (those can be filenames but can equally well be anything else really).

1.5.3 Items/structures (from native CS’s mesh) – the de facto format for those follows closely CS’s internal data model of mesh objects: each mesh object can contain submeshes in a hierarchical organisation; each mesh/submesh is defined through its geometry (as an enumeration of vertices and triangles made with those vertices) and attached materials that are in turn defined through textures, shaders and specific values for any shader variables used. Textures themselves are raster images with .dds as preferred format of CS (as other formats such as .png or .jpg effectively incur a penalty although at this point it’s unclear what this penalty adds up to in real terms). CS allows the creation and definition of meshes either as tedious enumerations of everything or through a few limited generators that tend to rely on regular shapes (e.g. spheres and cones). Both of those can be directly called from code or otherwise wrapped up in xml and “loaded” from code.

As everywhere else, the current client relies on xml for the definition of all possible types of meshes. It loads mesh definitions from the xml files in art/meshes.zip and expects that all entities ever required /encountered for the whole duration of the game reference one or several of the meshes in that archive. In turn, the xml in meshes.zip references of course materials that are -all of them!- expected to be found in art/materials.zip and are defined as such in art/materials.zip/materials.cslib xml file. The materials.cslib xml file first lists an inventory of textures (assigning a “name” id for each image file in the archive basically) and then defines various materials that make use of those textures and/or specify various shaders and corresponding values for the relevant shader variables. As usual, the shaders are simply referenced by name and assumed to exist as expected under that name, basically making art/materials.zip dependent implicitly on the whole contents of data/shader (and through that, further on to data/shader-snippets and data/renderlayers at the very least).

1.6 Sound
For sound, CS simply relies on external plugins for handling a few standard formats, most notably .ogg and .wav (apparently there are some .spx files too in data/voice but I haven’t seen them in use from anywhere within the current client’s code). PS adds on top of this a soundmanager that is intertwined with a whole bunch of code otherwise so getting to the heart of the simple matter of playing one sound file from the current client can be surprisingly messy. Still, from the point of view of data formats here, things are quite simple since not even PS managed to do much about sound files being simply .ogg, there is that.

1.7 GUI and icons

With respect to GUI and more generally any icons shown in game (so any 2D representations really, such as items in the inventory for instance), CS relies on external plugins. In principle there is CEGUI supported but the currently deployed client does not use that one, relying instead on PS’s own PAWS. Both CEGUI and PAWS share a design of GUI as a set of widgets but other than that I didn’t investigate CEGUI any further since the current client is rather deeply intertwined with PAWS. Consequently, the de facto data model for GUI is more of a reflection of what PAWS expects rather than anything else: art/skins contains .zip files that provide any number of .png images in whatever hierarchy of directory one sees fit, together with an imagelist.xml file that provides effectively the inventory, assigning a “resource name” to which file and listing the relative path to it. Note that this imagelist.xml as well as the art/skins location are both hardcoded in the current client. Moreover, the imagelist.xml is meant to be the full list of all possible icons and GUI elements in the game and known when the client starts since it’s all a pre-requisite of even creating the GUI widgets at all.

Besides the above, there’s another part to GUI, since the PS client further expects xml to be used (in data/gui) to define each and every window that can ever be shown by the client. Those make use of resources by name and as such are inevitably -and implicitly- dependent on the contents of art/skin. Moreover, some of the current code goes as far as referencing directly and hardcodedly resources from that same art/skin (most notably the mouse pointer icon). Not sure where exactly should this go into a “definition of the de facto data model” but there it is.

A rather funny part of the xml reference by name of various resources is that font resources (data/ttf currently) get the same treatment and the xml widget definitions that dully specify – thus fixing too! – their favourite fonts by name again. Then the legacy PS code also hardcodes a font as default in quite a few places and rather central to the GUI places at that (src/common/effects/pseffectobjtext.cpp and pseffectobjtext2d.cpp in the same place; src/common/paws/pawswidget.cpp and src/client/gui/psmainwidget.cpp), specifying the exact “data/ttf/reteprelieum.ttf” path as well. Except the actual file in data/ttf is called “reteprelleum” rather than the hardcoded “reteprelieum” and so errors are thrown and whatever else gets used instead for all the usefulness of that careful hardcoding of one font in that many places. Are you laughing yet?

  1. In particular fixed, finite and fully known upfront world and assets vs flexible, infinite and continuously discovered world and assets.[]
  2. Isn’t this what you always thought “choice” meant in practice, namely choosing between the inexistent and the impalatable? No?[]
  3. CrystalSpace[]
  4. Planeshift, the ancestor of the current client’s code, such as it is.[]
  5. More like accidents than formats.[]
  6. Let me add here the very clear and useful definition of this definitive: mircea_popescu: it’s definitive in the sense death&damnation is definitive, not in the sense mathematics is definitive.[]
  7. Yeah, I tried it out, I have the draft to show for it. And it was even a good rant really but not exactly serving the purpose it was meant to serve, so I discarded it anyway.[]
  8. It does sound like an interesting choice when set out this way, doesn’t it? The code you run on your own machine is meant to know more than you, to choose what to show you at one time or another and even to specifically thwart any attempts – cheater! – to get to know more than it decides to share with you.[]
  9. There is also “Collection” as yet another way of grouping resources based on whatever reason the programmer may come up with but this is not used by the current client at all so I won’t go into detail on it.[]
  10. Shared Class Facility[]

January 16, 2020

CrystalSpace: Creating Factories and Meshes, Setting the Client Down

Filed under: Coding,Eulora — Diana Coman @ 2:12 pm

Backtracking on a year’s work of cleaning swamps comes with the added dubious advantage of having even more work to do in order to set down the results such as they are for potential pickup (maybe!) sometime in the near or even not so near future. So I took a few deep breaths, checked the full compilation and versions of everything, read through the main changes all again and unloaded the main gist of it all in a readme file that might come in handy at a later time 1. My initial plan was to pack it all up and let it be really but since I did already the work of disentangling the CrystalSpace (CS) way of programmatically creating the whole map and a lot of the stuff in it, I might as well publish the prototype code as ugly as it is, what can it hurt anyway.

As the freshly baked readme file says, this prototype client is an attempted switch from legacy ps code to:

  1. SMG communication protocol as opposed to PS’s net/messages.h approach. Status: working registration of new account and exchange of Serpent keys for both ways communication + all lower level required functionality of the protocol (e.g. packing/unpacking, encryption/decryption, message types, queues). Existing (in EuCore, which is client’s lib for communication with the server and data acquisition, essentially SMG Comms + data acquisition skeleton) implementation for message types that are fully defined so far: keys management (rsa and serpent packaged messages), file transfer and request.
  2. Compiling with gprbuild (gnat) as opposed to current dependency on jam/ftjam. Status: working for client and eucore lib (though CS not ported to gprbuild) with the caveat that a few plugins haven’t yet been moved to gprbuild as well (mainly because they were anyway slated for removal e.g. bgloader aka PS’s background loader thing).
  3. On the fly loading of graphics (from clientcache/ for now for testing purposes) as opposed to the bgloader plugin delayed loaders that expected the whole WORLD to be known upfront and never changing. Status: removed the original “preloading” and caching of all sorts, as well as the whole “art” dir; existing working prototype loaders 2 integrated into the client code (aka in use) for sectors, factories, meshes of various sorts, textures, materials (including shader variables setting). Some of the prototype loaders test successfully the intended reliance on EuCore (Ada code) for data acquisition as well as on the fly loading of local files from any specified location (as opposed to predefined world.zip file in a set location). No world file needed or used anymore; no preloading/delayed loading (bgloader is not called anymore, nor expected although the code is left in src/plugins/common/bgloader); disabled also the part that was expecting the full list of all art assets including shaders to be known upfront and loaded before even getting into the game at all.
  4. Simplified start of the client meaning getting directly into game + using the EuCore lib (Ada implementation of SMG Comms as well as RSA TMSR, config file reading, key generation, read/write keys to binary files on disk etc). Status: working and tested, quick launch of the game and straight load of the world as known from client’s own local cache; removed dependency on net/ (though the code remains in src/common/net) and working offline client that doesn’t require ping-pong with the server for anything. All attempted communications with the server happens from EuCore only and it’s decided on by EuCore too since it is driven by the data acquisition needs, regardless of the GUI part itself. This version of the client is currently able to run standalone (ie no server really needed anywhere) and it will show a naked char that can be moved around (though none of the animations are loaded so that the char will remain stiff while gliding across the world).
  5. Removed preloading of 2D assets too (the interface itself, hence PAWS). Status: PAWS (widgets and therefore all buttons and windows inside the game) IS working but some windows may not be visible as they are not loaded (they have a LONG and INTRICATE codependency relation going on so it was cut at the root for starters); there is a tested prototype loader for icons (2D) but it’s currently not called from any of the running code (mainly because the issue to sort out first here is related to purging preloaded ideas from PAWS and PAWS windows themselves since currently it all cascades if one tries to load one single window). Notably, the Quit button works (if it’s invisible nevertheless) and the confirmation dialog window is one of the few that actually shows.
  6. Reading of config from Ada and from an .ini file as opposed to Planeshift’s multiple and multi-stratified configs. Status: working and tested for various data types; allows comments as well.

Caveats of this prototype client:

  1. This version will BLOCK (waiting to read from /dev/ttyUSB0) if there’s no FG (or other source of bits) connected and working on /dev/ttyUSB0 – this is because reading from FG is currently BLOCKING as implemented in EuCore lib
  2. The prototype implementations of various effects (e.g. rain, fire, snow) are currently commented out. They work as previously illustrated but I took them out at the moment as I was focusing on figuring out the shaders and their linking with materials and textures.
  3. The look of the terrain (aka the colours really) as well as the looks of various effects seems to depend on the exact environment where the code runs. I had the same code resulting in brown rain as well as nicely twinkling rain, depending on where I ran it. This was not fully investigated mainly for lack of time but it prompted the deeper dig into shaders as apparently the “nicer” aspects are mainly achieved through shader(s) use currently.
  4. The path & names of .bin key files are hardcoded and checked at runtime – depending on which keys are present and which ones are missing, the EuCore lib may try to generate new keys and/or send messages to the server to request an account/registration/etc.
  5. While the .gpr file works to build the client code, the EuCore lib is linked dynamically and has to be built separately (see its own gpr file). Moreover, the .gpr file relies on CRYSTAL and CAL3D environment variables to be set to the locations of CS and Cal3D respectively so it can include their headers as needed. At runtime, LD_LIBRARY_PATH still has to point to CS and Cal3d as well as EuCore’s locations.
  6. There are 3 libs that are not yet ported to gprbuild (mainly as they were either slated for removal or otherwise not yet high enough on the priority list to get done): soundmanager.so, celgraph.so and bgloader.so
  7. The tests for EuCore in eucore/tests have their own project that needs to be built separately.

As to the code, it’s ugly prototypes and messy and all that but anyway, perhaps best to start with the “basic” versions of Getters I wrote for CS’s “factories” of meshes, hoping that I could perhaps simplify a bit the full thing on this side. To understand what I’m talking about here: to create anything at all in CS, you’ll need first the “factory” for that type of thing since the idea is that it’s more efficient this way; which it is, sure, but only if you ever want to create a lot of very-similar-things in the same place like that, which happens ~never and so, as a result, all this does is that ~every thing you create ends up with its additional unique factory too!

Add to the above the maddening fact that CS uses strings – and it’s CPP code so strings are a pain at all times, of course – as IDs for everything so you have to search for factories as well as for objects by their *name*. Oh, and each factory is created through a call that requires also the *name* of the SCF plugin that provides the type of… factory you are after. Think of it: since you have factories to create “same sort of” objects, it follows that the factories themselves represent already types of objects, right? Sure, but why not have then also types of factories to make things more… hm, interesting? So there we go, there are also types of factories and plugins that implement (or not) those types and it’s true that in general there’s only one plugin for each type but don’t let that stop you from having the oh-so-useful-mechanism for having more than one type because there’s now no real cost to all this, no, none at all, right? Fine, first let’s set those plugin names to something shorter at least and give the “types” some numbers too, I like numbers:

#define DEFAULT_CULLER “crystalspace.culling.dynavis”

#define GENERIC_PLUGIN “crystalspace.mesh.object.genmesh”
#define CAL3D_PLUGIN “crystalspace.mesh.object.sprite.cal3d”
#define TERRAIN_PLUGIN “crystalspace.mesh.object.terrain2”
#define PARTICLES_PLUGIN “crystalspace.mesh.object.particles”
#define EMITTER_PLUGIN “crystalspace.mesh.object.particles.emitter”
#define EFFECTOR_PLUGIN “crystalspace.mesh.object.particles.effector”

#define TERRAIN_RENDERER “crystalspace.mesh.object.terrain2.bruteblockrenderer”
#define TERRAIN_DATAFEEDER_SIMPLE “crystalspace.mesh.object.terrain2.simpledatafeeder”
#define TERRAIN_DATAFEEDER_THREADED “crystalspace.mesh.object.terrain2.threadeddatafeeder” //alternative data feeder available in principle
#define TERRAIN_COLLIDER “crystalspace.mesh.object.terrain2.collider”

#define TEXTURE_FIRE “crystalspace.texture.type.fire”
#define TEXTURE_PLASMA “crystalspace.texture.type.plasma”
#define TEXTURE_WATER “crystalspace.texture.type.water”

#define FACTORY_NONE 0
#define FACTORY_GENERIC 1
#define FACTORY_CAL3D 2
#define FACTORY_TERRAIN 3
#define FACTORY_PARTICLES 4

Using the above, an example 3 of a “Getter” method that will retrieve an existing factory from the engine or otherwise create it and return it, if it doesn’t exist already:

csRef EuLoader::GetFactoryTerrain(char *factName) {
//terrain of a sector
csRef factWrapper = 0;

//check if the factory exists already
factWrapper = psengine->GetEngine()->FindMeshFactory(factName);

if (factWrapper != 0)
return factWrapper;

//create a basic factory of this type (aka using the plugin corresponding to this “type”)
//boolean last argument is “addtolist” so yes, it should add it to list
factWrapper = psengine->GetEngine()->CreateMeshFactory(TERRAIN_PLUGIN, factName, true);

return factWrapper;
}

Similar Getter method for a texture, using the given name as path too since one string is anyway enough of a mess in there, no need for two. The loader thing is simply CS’s way of reading an image file, since the texture here is simply assumed to be an image (.png, .bmp, .jpg, .dds are all supported):

csRef EuLoader::GetTexture( char *texName ) {
csRef tex = 0;

//first check if the engine already has it loaded
tex = psengine->GetEngine()->FindTexture(texName);
if (tex != 0)
return tex; //found it so return and be done with it.

//texture not found, so load it
csRef loader = csQueryRegistry (psengine->GetObjectRegistry());
if (loader == 0)
Err::NonfatalError(“EuLoader::LoadTexture failed to find the iLoader plugin of CS!\n”);
else
tex = loader->LoadTexture(texName, texName); //NB: the “name” is same as the full path!

return tex; //either way, the result is still in here
}

The equivalent for a Material is potentially more dubious because on one hand CS *anyway* wraps all textures into materials with the same name and on the other hand, a material also holds shader variables of all sorts, with the basic texture set as a “shader variable” itself! Moreover, shader variables can have different data types, what joy. So this one is not all that basic: it queries the client’s data cache (via that SMG object) to retrieve all shaders and variables for this material and applies them as they come:

csRef EuLoader::GetMaterial( char *matName) {
csRef matWrapper = 0;
csRef mat = 0;

//first check if the engine already has it loaded
matWrapper = psengine->GetEngine()->FindMaterial(matName);
if (matWrapper != 0)
return matWrapper; //material found so return it
//material not found, so create it;
//NB: “texture” in xml is ANYWAY made into a shader var at load time!
mat = psengine->GetEngine()->CreateBaseMaterial(0);
//add it to the engine’s list of materials
matWrapper = psengine->GetEngine()->GetMaterialList()->NewMaterial(mat, matName);

//set shaders and corresponding textures and/or shader vars
csRef stringSet = csQueryRegistryTagInterface (psengine->GetObjectRegistry(), “crystalspace.shader.variablenameset”);
csRef strings = csQueryRegistryTagInterface (psengine->GetObjectRegistry(), “crystalspace.shared.stringset”);
// csRef loader = csQueryRegistry (psengine->GetObjectRegistry());
uint32_t sindex = 1;
//for shaders to set on this material
char stype[MAX_STR_LEN+1];
char sname[MAX_STR_LEN+1];
unsigned int stypelen = MAX_STR_LEN;
unsigned int snamelen = MAX_STR_LEN;
while (psengine->SMG.GetShader(matName, sindex, stype, &stypelen, sname, &snamelen)) {
stype[stypelen] = ‘\0’;
sname[snamelen] = ‘\0’;
csStringID st = strings->Request(stype);
csRef ss = GetShader(sname);
if (ss != 0)
mat->SetShader(st, ss);
//next shader, if any
sindex = sindex + 1;
//reset the lengths!
stypelen = MAX_STR_LEN;
snamelen = MAX_STR_LEN;
}
//shader vars, if any
char sVarName[MAX_STR_LEN+1];
unsigned int sVarNameLen = MAX_STR_LEN;

//texture type shader vars
char sTexName[MAX_STR_LEN+1];
unsigned int sTexNameLen = MAX_STR_LEN;
sindex = 1;
while (psengine->SMG.GetShaderVar(matName, sindex, sVarName, &sVarNameLen, sTexName, &sTexNameLen)) {
sVarName[sVarNameLen] = ‘\0’;
sTexName[sTexNameLen] = ‘\0’;
csRef stex = GetTexture(sTexName);
if (stex != 0) {
csRef shaderVar;
CS::ShaderVarStringID strID = stringSet->Request(sVarName);
shaderVar.AttachNew( new csShaderVariable );
shaderVar->SetType(csShaderVariable::TEXTURE);
shaderVar->SetName(strID);
shaderVar->SetValue(stex);
mat->AddVariable(shaderVar); //as iMaterial extends directly iShaderVariableContext, this can be added here.
}
else
Err::NonfatalError(“EuLoader::GetMaterial can’t find the texture for a material\n”);
sindex = sindex + 1;
sVarNameLen = MAX_STR_LEN;
sTexNameLen = MAX_STR_LEN;
}

//float type shader vars
float v1, v2, v3, v4;
sindex = 1;
while (psengine->SMG.GetShaderVar(matName, sindex, sVarName, &sVarNameLen, &v1)) {
sVarName[sVarNameLen] = ‘\0’;
csRef shaderVar;
CS::ShaderVarStringID strID = stringSet->Request(sVarName);
shaderVar.AttachNew( new csShaderVariable );
shaderVar->SetType(csShaderVariable::FLOAT);
shaderVar->SetName(strID);
shaderVar->SetValue(v1);
mat->AddVariable(shaderVar); //as iMaterial extends directly iShaderVariableContext, this can be added here.
sindex = sindex + 1;
sVarNameLen = MAX_STR_LEN;
}
//vector2 type shader vars
sindex = 1;
while (psengine->SMG.GetShaderVar(matName, sindex, sVarName, &sVarNameLen, &v1, &v2)) {
sVarName[sVarNameLen] = ‘\0’;
csRef shaderVar;
CS::ShaderVarStringID strID = stringSet->Request(sVarName);
shaderVar.AttachNew( new csShaderVariable );
shaderVar->SetType(csShaderVariable::VECTOR2);
shaderVar->SetName(strID);
shaderVar->SetValue(csVector2(v1, v2));
mat->AddVariable(shaderVar); //as iMaterial extends directly iShaderVariableContext, this can be added here.
sindex = sindex + 1;
sVarNameLen = MAX_STR_LEN;
}

//vector3 type shader vars
sindex = 1;
while (psengine->SMG.GetShaderVar(matName, sindex, sVarName, &sVarNameLen, &v1, &v2, &v3)) {
sVarName[sVarNameLen] = ‘\0’;
csRef shaderVar;
CS::ShaderVarStringID strID = stringSet->Request(sVarName);
shaderVar.AttachNew( new csShaderVariable );
shaderVar->SetType(csShaderVariable::VECTOR3);
shaderVar->SetName(strID);
shaderVar->SetValue(csVector3(v1, v2, v3));
mat->AddVariable(shaderVar); //as iMaterial extends directly iShaderVariableContext, this can be added here.
sindex = sindex + 1;
sVarNameLen = MAX_STR_LEN;
}

//vector4 shader variables
sindex = 1;
while (psengine->SMG.GetShaderVar(matName, sindex, sVarName, &sVarNameLen, &v1, &v2, &v3, &v4)) {
sVarName[sVarNameLen] = ‘\0’;
csRef shaderVar;
CS::ShaderVarStringID strID = stringSet->Request(sVarName);
shaderVar.AttachNew( new csShaderVariable );
shaderVar->SetType(csShaderVariable::VECTOR4);
shaderVar->SetName(strID);
shaderVar->SetValue(csVector4(v1, v2, v3, v4));
mat->AddVariable(shaderVar); //as iMaterial extends directly iShaderVariableContext, this can be added here.
sindex = sindex + 1;
sVarNameLen = MAX_STR_LEN;
}
return matWrapper; //the result is in matWrapper at all times
}

Using the above, a prototype method that creates a sky as a sphere with the fixed “starry” image painted on the inside, since your world is – of course! – all inside of the sky dome, in this grand vision of computer graphics 4. Note that both the texture and the location (aka sector) of the sky are hardcoded in this hasty prototype:

bool EuLoader::GetSky() {
char skydomef[] = “skydomef”;
//factory wrapper
csRef factSkyWrapper = GetFactoryMesh(skydomef); //a *generic* mesh factory, used for ALL non-moving objects

if (factSkyWrapper == 0) {
Err::FatalError(“FAILED TO GET GENERIC FACTORY WRAPPER TO MAKE THE SKY!\n”);
return false; //this should never happen….
}
//geometry HAS to be set on the factory state as the meshstate lacks the needed methods…
//…and to get the factory state, first need the factory rather than the wrapper, arghhh
csRef factSky = factSkyWrapper->GetMeshObjectFactory();
csRef factClouds = factCloudsWrapper->GetMeshObjectFactory();
if (factSky == 0) {
Err::NonfatalError(“Sky/Clouds failed to find iMeshObjectFactory valid for created factory\n”);
return false;
}

//and since anyway can’t really have several objects here, set the MATERIAL on factory as well….ugh.
char skyMatName[] = “stars.png”;
csRef skyMat = GetMaterial(skyMatName); //this WILL load/create needed texture too, if it does not exist already
if (skyMat == 0) {
Err::NonfatalError(“EuLoader:GetFactory failed to find material for sky\n”);
return false;
}
factSky->SetMaterialWrapper(skyMat); //sets the material for the whole factory i.e. default for objects created from this factory

//generic settings
factSky->GetFlags().SetBool(CS_FACTORY_STATICSHAPE, true); //default static for genmeshes; non-static should use CAL3D!
factClouds->GetFlags().SetBool(CS_FACTORY_STATICSHAPE, true); //default static for genmeshes; non-static should use CAL3D!
//defaults: compress = false; auto_normals = false; auto_normals_nocompress= false
//if compress state->Compress();
//if auto_normals state->CalculateNormals(!auto_normals_nocompress);

//factory state
csRef factStateSky = scfQueryInterface (factSky);

//create sky dome geometry
using namespace CS::Geometry;
csVector3 center (0, 0, 0);
int rim_vertices = 16;
bool toponly = false; //make FULL sphere, not only top half; default=false
bool reversed = true; //so it’s visible from INSIDE, yes; default=false
bool cylmapping = false; //no cylindrical texture mapping; default=false
csEllipsoid ellips;
ellips.SetRadius(csVector3(2500,2500,2500));
ellips.SetCenter(center);
//this should directly generate the vertices,texels,normals and triangles + set vertex colors to black
factStateSky->GenerateSphere(ellips, rim_vertices, cylmapping, toponly, reversed);

/* alternative way to the above neater GenerateSphere? :
csDirtyAccessArray mesh_vertices;
csDirtyAccessArray mesh_texels;
csDirtyAccessArray mesh_normals;
csDirtyAccessArray mesh_triangles;
Primitives::GenerateSphere (ellips, rim_vertices,
mesh_vertices, mesh_texels,
mesh_normals, mesh_triangles, cylmapping,
toponly, reversed, 0);
AppendOrSetData (factState, mesh_vertices, mesh_texels,
mesh_normals, mesh_triangles);
*/

//directly here instead of calling “GeneralMeshBuilder::CreateMesh” that does exactly this;
//see cs/libs/cstool/genmeshbuilder.cpp

//sector – should get this either as param or from smg….TO DO
csRef s = psengine->GetEngine()->FindSector(“Rotces”);
if (s == 0) {
//this should NOT happen as the sector should call/create contained entities, not the other way around.
Err::NonfatalError(“EuLoader: failed to find the sector for the sky!\n”);
return false;
}

//objects (?)
//apparently: it’s really the triangles and/or render buffer stuff i.e. the “raw” values that just aren’t right (it WORKS with a generated ellipse…)
char skyn[] = “skyn”;
csRef skyMeshWrapper = psengine->GetEngine()->CreateMeshWrapper (factSkyWrapper, skyn, s, csVector3 (0));

//z buffer and priorities
skyMeshWrapper->SetZBufMode(CS_ZBUF_USE); //DO read so that the zbuffer gets updated…ugh;only write to the z-buffer but do not read
skyMeshWrapper->SetRenderPriority(psengine->GetEngine()->GetObjectRenderPriority());

psengine->GetEngine()->PrecacheMesh(skyMeshWrapper); //hmm?

return (skyMeshWrapper != 0 );
}

For an example, the test made with the moon, effectively creating an object in the world directly through the list of vertices and texture coordinates (as taken from its existing xml file otherwise, don’t look weirdly at me when you get to that huge list of vertices, they have been there all the time):

csRef EuLoader::GetMoon() {

csRef fact;
csRef state;
csRef type;

type = csLoadPluginCheck ( psengine->GetObjectRegistry(), GENERIC_PLUGIN, false);
if (!type)
Err::FatalError(“EuLOADER: can’t find crystalspace.mesh.object.genmesh plugin\n”);

fact = type->NewFactory ();
state = scfQueryInterface (fact);

//material
char matname[11] = “waves.dds”;
csRef mat = GetMaterial(matname);
if (mat == 0)
Err::FatalError(“EuLOADER: can’t find material waves.dds”);
fact->SetMaterialWrapper(mat);

//renderbuffer: position
const float posElems[] = {-24.46479416,4.86635208,16.66710281,-20.80559731,4.13849306,21.21320534,-21.21320534,0.00000000,21.21320343,-24.94408798,0.00000000,16.66710663,-20.80559731,4.13849258,-21.21320343,-24.46479607,4.86635256,-16.66710663,-24.94409180,0.00000000,-16.66710663,-21.21320343,0.00000000,-21.21320343,-27.18382263,5.40720034,11.48050213,-27.71638680,0.00000000,11.48050308,-16.34685135,3.25159287,-24.94409180,-16.66710663,0.00000000,-24.94409180,-28.85818863,5.74025345,5.85270882,-29.42355728,0.00000000,5.85271072,-11.25990391,2.23973608,-27.71638680,-11.48049831,0.00000000,-27.71638680,-29.42355919,5.85271168,0.00000358,-30.00000000,0.00000000,0.00000226,-11.25990868,2.23973703,27.71638870,-5.74025154,1.14180899,29.42355728,-5.85270977,0.00000000,29.42355728,-11.48050308,0.00000000,27.71638680,-5.74024439,1.14180756,-29.42355919,-5.85270309,0.00000000,-29.42355919,-28.85819244,5.74025393,-5.85270500,-29.42355919,0.00000000,-5.85270643,-16.34685326,3.25159311,24.94408607,-16.66710663,0.00000000,24.94408798,-27.18382263,5.40720034,-11.48049831,-27.71638680,0.00000000,-11.48049831,-10.60659599,4.39340019,-27.71638680,-15.39839554,6.37822866,-24.94409180,-16.34685135,3.25159287,-24.94409180,-11.25990391,2.23973608,-27.71638680,-27.71638489,11.48050785,0.00000358,-27.18381882,11.25991058,5.85270882,-10.60660172,4.39340210,27.71638870,-5.40719748,2.23973894,29.42355728,-5.40719128,2.23973608,-29.42355919,-5.74024439,1.14180756,-29.42355919,-27.18381882,11.25991058,-5.85270500,-15.39839745,6.37822914,24.94408607,-25.60660172,10.60660458,-11.48049831,-19.59844398,8.11794472,21.21320534,-23.04533005,9.54569340,-16.66710663,-23.04533005,9.54569244,16.66710281,-19.59844208,8.11794472,-21.21320343,-25.60660172,10.60660458,11.48050213,-15.39839554,6.37822866,-24.94409180,-17.63813210,11.78542995,-21.21320343,-20.74024582,13.85819817,-16.66710663,-23.04533005,9.54569340,-16.66710663,-19.59844208,8.11794472,-21.21320343,-23.04533005,15.39840508,11.48050213,-20.74024582,13.85819626,16.66710281,-23.04533005,9.54569244,16.66710281,-25.60660172,10.60660458,11.48050213,-13.85818863,9.25975227,-24.94409180,-24.46478844,16.34685707,5.85270882,-27.18381882,11.25991058,5.85270882,-9.54568291,6.37822866,-27.71638680,-24.94408607,16.66711426,0.00000358,-27.71638489,11.48050785,0.00000358,-9.54568672,6.37823105,27.71638870,-4.86634779,3.25159669,29.42355728,-4.86634254,3.25159264,-29.42355919,-24.46478844,16.34685707,-5.85270500,-27.18381882,11.25991058,-5.85270500,-13.85818958,9.25975323,24.94408607,-15.39839745,6.37822914,24.94408607,-23.04533005,15.39840508,-11.48049831,-25.60660172,10.60660458,-11.48049831,-17.63813210,11.78542995,21.21320534,-19.59844398,8.11794472,21.21320534,-19.59844017,19.59844971,-11.48049831,-20.80558968,20.80560303,-5.85270500,-14.99999523,15.00000477,21.21320534,-11.78542042,11.78543091,24.94408607,-17.63812828,17.63813782,-16.66710663,-17.63812637,17.63813782,16.66710281,-14.99999428,15.00000477,-21.21320343,-19.59844017,19.59844971,11.48050213,-11.78541756,11.78542995,-24.94409180,-20.80558777,20.80560303,5.85270882,-8.11793423,8.11794472,-27.71638680,-21.21319962,21.21320915,0.00000358,-8.11793709,8.11794853,27.71638870,-4.13848686,4.13849735,29.42355728,-4.13848305,4.13849211,-29.42355919,-6.37821770,9.54569435,-27.71638680,-9.25973988,13.85819817,-24.94409180,-16.66710091,24.94409561,0.00000358,-16.34684372,24.46479797,5.85270882,-6.37821913,9.54569721,27.71638870,-3.25158596,4.86635780,29.42355728,-3.25158310,4.86635208,-29.42355919,-16.34684372,24.46479797,-5.85270500,-9.25974178,13.85819912,24.94408607,-15.39839363,23.04533958,-11.48049831,-11.78541756,17.63813972,21.21320534,-13.85818386,20.74025345,-16.66710663,-13.85818291,20.74025345,16.66710281,-11.78541660,17.63813972,-21.21320343,-15.39839363,23.04533958,11.48050213,-9.54567719,23.04533386,16.66710281,-8.11793327,19.59844971,21.21320534,-8.11793232,19.59844971,-21.21320343,-9.54567909,23.04533386,-16.66710663,-10.60659409,25.60660934,11.48050213,-6.37821627,15.39840508,-24.94409180,-11.25989437,27.18382645,5.85270882,-4.39338970,10.60660553,-27.71638680,-11.48049545,27.71639252,0.00000358,-4.39339066,10.60660839,27.71638870,-2.23972821,5.40720701,29.42355728,-2.23972631,5.40719986,-29.42355919,-11.25989437,27.18382645,-5.85270500,-6.37821770,15.39840698,24.94408607,-10.60659409,25.60660934,-11.48049831,-5.74023724,28.85819435,-5.85270500,-5.85270023,29.42356491,0.00000358,-3.25158167,16.34685898,24.94408607,-2.23972559,11.25991631,27.71638870,-5.40718985,27.18382835,-11.48049831,-4.13848162,20.80560493,21.21320534,-4.86633825,24.46479797,-16.66710663,-4.86633682,24.46479797,16.66710281,-4.13847971,20.80560493,-21.21320343,-5.40718985,27.18382835,11.48050213,-3.25158072,16.34685898,-24.94409180,-5.74023724,28.85819435,5.85270882,-2.23972535,11.25991249,-27.71638680,-1.14179850,5.74026012,29.42355728,-1.14179790,5.74025249,-29.42355919,0.00001179,16.66711044,-24.94409180,0.00001201,21.21320724,-21.21320343,0.00001425,29.42355728,5.85270882,0.00001022,27.71639252,11.48050213,0.00001067,11.48050690,-27.71638680,0.00001022,30.00000381,0.00000358,0.00001089,11.48050880,27.71638870,0.00001033,5.85271788,29.42355728,0.00000944,5.85271120,-29.42355919,0.00001425,29.42355728,-5.85270500,0.00001089,16.66711426,24.94408607,0.00001022,27.71639252,-11.48049831,0.00001022,21.21320724,21.21320534,0.00001246,24.94408798,-16.66710663,0.00001425,24.94408798,16.66710281,4.86636257,24.46479416,-16.66710663,5.40721035,27.18382835,-11.48049831,4.86636448,24.46479416,16.66710281,4.13850212,20.80560303,21.21320534,4.13850355,20.80560303,-21.21320343,5.40721035,27.18382835,11.48050213,3.25160384,16.34685707,-24.94409180,5.74026585,28.85818863,5.85270882,2.23974657,11.25991058,-27.71638680,5.85272074,29.42356110,0.00000358,2.23974705,11.25991440,27.71638870,1.14181888,5.74025917,29.42355728,1.14181662,5.74025249,-29.42355919,5.74026585,28.85818863,-5.85270500,3.25160384,16.34685898,24.94408607,2.23974514,5.40719986,-29.42355919,4.39341021,10.60660362,-27.71638680,11.25992203,27.18381882,-5.85270500,11.48051453,27.71638680,0.00000358,6.37823963,15.39840508,24.94408607,4.39341116,10.60660553,27.71638870,10.60661411,25.60660553,-11.48049831,8.11795330,19.59844780,21.21320534,9.54570103,23.04533005,-16.66710663,9.54570293,23.04533005,16.66710281,8.11795425,19.59844780,-21.21320343,10.60661411,25.60660553,11.48050213,6.37823963,15.39840126,-24.94409180,11.25992203,27.18381882,5.85270882,2.23974848,5.40720558,29.42355728,9.25976276,13.85819435,-24.94409180,11.78543854,17.63813782,-21.21320343,16.34686470,24.46478844,5.85270882,15.39840889,23.04533386,11.48050213,6.37823772,9.54569054,-27.71638680,16.66711807,24.94408798,0.00000358,6.37823963,9.54569340,27.71638870,3.25160551,4.86635590,29.42355728,3.25160146,4.86635113,-29.42355919,16.34686470,24.46478844,-5.85270500,9.25976372,13.85819817,24.94408607,15.39840889,23.04533386,-11.48049831,11.78543854,17.63813782,21.21320534,13.85820389,20.74024582,-16.66710663,13.85820484,20.74024582,16.66710281,17.63814163,17.63813019,-16.66710663,19.59845543,19.59844780,-11.48049831,17.63814545,17.63812828,16.66710281,15.00001144,15.00000191,21.21320534,15.00001144,15.00000000,-21.21320343,19.59845543,19.59844780,11.48050213,11.78543854,11.78542328,-24.94409180,20.80560684,20.80559158,5.85270882,8.11795330,8.11794281,-27.71638680,21.21321487,21.21320343,0.00000358,8.11795616,8.11794376,27.71638870,4.13850594,4.13849545,29.42355728,4.13850117,4.13849115,-29.42355919,20.80560684,20.80559158,-5.85270500,11.78544044,11.78542900,24.94408607,4.86636066,3.25159168,-29.42355919,9.54570103,6.37822628,-27.71638680,24.46480179,16.34684753,-5.85270500,24.94409752,16.66710281,0.00000358,13.85820866,9.25974941,24.94408607,9.54570389,6.37822676,27.71638870,23.04534531,15.39840126,-11.48049831,17.63814735,11.78542519,21.21320534,20.74026108,13.85818958,-16.66710663,20.74025917,13.85818672,16.66710281,17.63814545,11.78542328,-21.21320343,23.04534531,15.39840126,11.48050213,13.85820580,9.25974751,-24.94409180,24.46480179,16.34684753,5.85270882,4.86636591,3.25159431,29.42355728,15.39840889,6.37822390,-24.94409180,19.59845352,8.11793995,-21.21320343,27.18382835,11.25990295,5.85270882,25.60661507,10.60660267,11.48050213,10.60661316,4.39339828,-27.71638680,27.71639252,11.48049927,0.00000358,10.60661507,4.39339828,27.71638870,5.40721416,2.23973656,29.42355728,5.40720892,2.23973536,-29.42355919,27.18382835,11.25990295,-5.85270500,15.39841461,6.37822628,24.94408607,25.60661507,10.60660267,-11.48049831,19.59845543,8.11794090,21.21320534,23.04533958,9.54568481,-16.66710663,23.04533768,9.54568291,16.66710281,20.80560875,4.13848925,21.21320534,16.34686661,3.25159049,24.94408607,24.46479988,4.86634684,-16.66710663,27.18383598,5.40719891,-11.48049831,24.46479797,4.86634541,16.66710281,20.80560684,4.13848877,-21.21320343,27.18383598,5.40719891,11.48050213,16.34686470,3.25158882,-24.94409180,28.85820007,5.74024677,5.85270882,11.25991917,2.23973489,-27.71638680,29.42356491,5.85270643,0.00000358,11.25992298,2.23973417,27.71638870,5.74026775,1.14180732,29.42355728,5.74026108,1.14180708,-29.42355919,28.85820007,5.74024677,-5.85270500,30.00000381,-0.00000313,0.00000358,29.42356300,-0.00000447,5.85270882,11.48051643,-0.00000156,27.71638870,5.85272551,-0.00000101,29.42355728,5.85271931,0.00000000,-29.42355919,11.48051453,-0.00000067,-27.71638680,29.42356300,-0.00000447,-5.85270500,16.66711998,-0.00000201,24.94408607,27.71639824,-0.00000134,-11.48049831,21.21321487,-0.00000179,21.21320534,24.94409370,-0.00000313,-16.66710663,24.94409180,-0.00000402,16.66710281,21.21321106,-0.00000179,-21.21320343,27.71639824,-0.00000134,11.48050213,16.66711617,-0.00000291,-24.94409180,20.80560684,-4.13849258,-21.21320343,24.46479797,-4.86635256,-16.66710663,27.18383408,-5.40720034,11.48050213,24.46479797,-4.86635303,16.66710281,16.34686279,-3.25159431,-24.94409180,28.85819626,-5.74025536,5.85270882,11.25991917,-2.23973608,-27.71638680,29.42356110,-5.85271168,0.00000358,11.25992203,-2.23973727,27.71638870,5.74026680,-1.14180923,29.42355728,5.74026108,-1.14180696,-29.42355919,28.85819626,-5.74025536,-5.85270500,16.34686661,-3.25159431,24.94408607,27.18383408,-5.40720034,-11.48049831,20.80560684,-4.13849306,21.21320534,25.60661125,-10.60660362,-11.48049831,27.18382645,-11.25991058,-5.85270500,19.59845352,-8.11794376,21.21320534,15.39840889,-6.37823009,24.94408607,23.04533386,-9.54569054,-16.66710663,23.04533386,-9.54569054,16.66710281,19.59844971,-8.11794376,-21.21320343,25.60661125,-10.60660362,11.48050213,15.39840698,-6.37822866,-24.94409180,27.18382645,-11.25991058,5.85270882,10.60661316,-4.39339924,-27.71638680,27.71638680,-11.48050308,0.00000358,10.60661411,-4.39340115,27.71638870,5.40721321,-2.23973846,29.42355728,5.40720844,-2.23973489,-29.42355919,9.54570007,-6.37822676,-27.71638680,13.85820007,-9.25975037,-24.94409180,24.94408798,-16.66710663,0.00000358,24.46479607,-16.34685516,5.85270882,9.54570103,-6.37822866,27.71638870,4.86636400,-3.25159550,29.42355728,4.86635971,-3.25159097,-29.42355919,24.46479607,-16.34685516,-5.85270500,13.85820389,-9.25975227,24.94408607,23.04534149,-15.39840126,-11.48049831,17.63814163,-11.78542614,21.21320534,20.74025345,-13.85819244,-16.66710663,20.74025154,-13.85819244,16.66710281,17.63813972,-11.78542614,-21.21320343,23.04534149,-15.39840126,11.48050213,13.85820007,-9.25975037,-24.94409180,15.00000763,-15.00000000,-21.21320343,17.63813782,-17.63813210,-16.66710663,19.59845352,-19.59844780,11.48050213,17.63813400,-17.63813210,16.66710281,11.78543091,-11.78542614,-24.94409180,20.80559731,-20.80559731,5.85270882,8.11795139,-8.11794281,-27.71638680,9.54570007,-6.37822676,-27.71638680,21.21320343,-21.21319962,0.00000358,8.11795235,-8.11794472,27.71638870,4.13850307,-4.13849545,29.42355728,4.13849926,-4.13849020,-29.42355919,4.86635971,-3.25159097,-29.42355919,20.80559731,-20.80559731,-5.85270500,11.78543472,-11.78542900,24.94408607,19.59845352,-19.59844780,-11.48049831,15.00000954,-15.00000000,21.21320534,15.39840698,-23.04533386,-11.48049831,16.34685326,-24.46479034,-5.85270500,11.78543186,-17.63813210,21.21320534,9.25975704,-13.85819626,24.94408607,13.85819626,-20.74024582,-16.66710663,13.85819530,-20.74024582,16.66710281,11.78543091,-17.63813210,-21.21320343,15.39840698,-23.04533386,11.48050213,9.25975609,-13.85819340,-24.94409180,16.34685326,-24.46479034,5.85270882,6.37823582,-9.54569054,-27.71638680,16.66710663,-24.94408035,0.00000358,6.37823582,-9.54569340,27.71638870,3.25160289,-4.86635494,29.42355728,3.25160027,-4.86634970,-29.42355919,4.39340830,-10.60660267,-27.71638680,6.37823200,-15.39839935,-24.94409180,11.48050404,-27.71637917,0.00000358,11.25990868,-27.18381882,5.85270882,4.39340734,-10.60660458,27.71638870,2.23974562,-5.40720367,29.42355728,2.23974419,-5.40719748,-29.42355919,11.25990868,-27.18381882,-5.85270500,6.37823343,-15.39840126,24.94408607,10.60660839,-25.60660362,-11.48049831,8.11795044,-19.59844398,21.21320534,9.54569435,-23.04532814,-16.66710663,9.54569244,-23.04532623,16.66710281,8.11794758,-19.59844398,-21.21320343,10.60660839,-25.60660362,11.48050213,4.13849735,-20.80559731,-21.21320343,4.86635590,-24.46478844,-16.66710663,5.40720463,-27.18382645,11.48050213,4.86635447,-24.46478653,16.66710281,3.25159764,-16.34685135,-24.94409180,5.74025345,-28.85818481,5.85270882,2.23974466,-11.25990868,-27.71638680,5.85271311,-29.42354965,0.00000358,2.23974323,-11.25991058,27.71638870,1.14181626,-5.74025631,29.42355728,1.14181614,-5.74025059,-29.42355919,5.74025345,-28.85818481,-5.85270500,3.25159836,-16.34685516,24.94408607,5.40720463,-27.18382645,-11.48049831,4.13849926,-20.80559731,21.21320534,0.00000687,-16.66710663,24.94408607,0.00000754,-11.48050499,27.71638870,0.00000530,-27.71638680,-11.48049831,0.00000307,-29.42354965,-5.85270500,0.00000843,-21.21320343,21.21320534,0.00000665,-24.94408035,-16.66710663,0.00000620,-24.94407845,16.66710281,0.00000665,-21.21320343,-21.21320343,0.00000530,-27.71638680,11.48050213,0.00000642,-16.66710281,-24.94409180,0.00000307,-29.42354965,5.85270882,0.00000933,-11.48050308,-27.71638680,0.00000486,-29.99998856,0.00000358,0.00000832,-5.85271358,29.42355728,0.00000944,-5.85270786,-29.42355919,-5.74024677,-28.85818100,5.85270882,-5.40719318,-27.18382263,11.48050213,-2.23972583,-11.25990868,-27.71638680,-3.25158405,-16.34685135,-24.94409180,-5.85270309,-29.42354202,0.00000358,-2.23972797,-11.25991058,27.71638870,-1.14179957,-5.74025536,29.42355728,-1.14179730,-5.74024963,-29.42355919,-5.74024677,-28.85818100,-5.85270500,-3.25158453,-16.34685326,24.94408607,-5.40719318,-27.18382263,-11.48049831,-4.13848162,-20.80559540,21.21320534,-4.86634207,-24.46478653,-16.66710663,-4.86634207,-24.46478271,16.66710281,-4.13848352,-20.80559540,-21.21320343,-9.54567909,-23.04532242,-16.66710663,-10.60659599,-25.60660172,-11.48049831,-9.54567909,-23.04532051,16.66710281,-8.11793232,-19.59844208,21.21320534,-8.11793327,-19.59844017,-21.21320343,-10.60659599,-25.60660172,11.48050213,-6.37821770,-15.39839363,-24.94409180,-11.25990009,-27.18380928,5.85270882,-4.39338923,-10.60660267,-27.71638680,-11.48049355,-27.71636963,0.00000358,-4.39339113,-10.60660362,27.71638870,-2.23972821,-5.40720177,29.42355728,-2.23972464,-5.40719604,-29.42355919,-11.25990009,-27.18380928,-5.85270500,-6.37821913,-15.39839745,24.94408607,-3.25158024,-4.86634779,-29.42355919,-6.37821674,-9.54568958,-27.71638680,-16.34684372,-24.46478081,-5.85270500,-16.66709518,-24.94406891,0.00000358,-9.25974083,-13.85819054,24.94408607,-6.37821913,-9.54569054,27.71638870,-15.39839363,-23.04533005,-11.48049831,-11.78541374,-17.63813210,21.21320534,-13.85818005,-20.74024200,-16.66710663,-13.85817909,-20.74024010,16.66710281,-11.78541660,-17.63813019,-21.21320343,-15.39839363,-23.04533005,11.48050213,-9.25973892,-13.85818672,-24.94409180,-16.34684372,-24.46478081,5.85270882,-3.25158477,-4.86635256,29.42355728,-11.78541374,-11.78541756,-24.94409180,-14.99999046,-14.99999523,-21.21320343,-20.80558395,-20.80558395,5.85270882,-19.59843826,-19.59844208,11.48050213,-8.11793232,-8.11794090,-27.71638680,-21.21318817,-21.21318626,0.00000358,-8.11793423,-8.11794186,27.71638870,-4.13848448,-4.13849211,29.42355728,-4.13847923,-4.13848829,-29.42355919,-20.80558395,-20.80558395,-5.85270500,-11.78541660,-11.78542137,24.94408607,-19.59843826,-19.59844208,-11.48049831,-14.99999046,-14.99999809,21.21320534,-17.63811874,-17.63812447,-16.66710663,-17.63811874,-17.63812256,16.66710281,-20.74023438,-13.85818481,-16.66710663,-23.04532623,-15.39839554,-11.48049831,-20.74023438,-13.85818291,16.66710281,-17.63812447,-11.78542233,21.21320534,-17.63812447,-11.78541946,-21.21320343,-23.04532623,-15.39839554,11.48050213,-13.85818291,-9.25974178,-24.94409180,-24.46477699,-16.34683990,5.85270882,-9.54568005,-6.37822533,-27.71638680,-24.94406700,-16.66708946,0.00000358,-9.54568291,-6.37822580,27.71638870,-4.86634350,-3.25159192,29.42355728,-4.86633825,-3.25158954,-29.42355919,-24.46477699,-16.34683990,-5.85270500,-13.85818386,-9.25974560,24.94408607,-5.40718555,-2.23973346,-29.42355919,-10.60659313,-4.39339733,-27.71638680,-27.18380356,-11.25989723,-5.85270500,-27.71636200,-11.48048782,0.00000358,-15.39838982,-6.37822247,24.94408607,-10.60659409,-4.39339733,27.71638870,-25.60659409,-10.60659599,-11.48049831,-19.59843445,-8.11793995,21.21320534,-23.04531288,-9.54568100,-16.66710663,-23.04531288,-9.54568005,16.66710281,-19.59843445,-8.11793613,-21.21320343,-25.60659409,-10.60659599,11.48050213,-15.39838696,-6.37821913,-24.94409180,-27.18380356,-11.25989723,5.85270882,-5.40719223,-2.23973489,29.42355728,-16.34683800,-3.25158548,-24.94409180,-20.80558586,-4.13848591,-21.21320343,-28.85816956,-5.74024248,5.85270882,-27.18381500,-5.40719318,11.48050213,-11.25989914,-2.23973393,-27.71638680,-29.42353058,-5.85269690,0.00000358,-11.25990105,-2.23973370,27.71638870,-5.74024439,-1.14180589,29.42355728,-5.74023771,-1.14180613,-29.42355919,-28.85816956,-5.74024248,-5.85270500,-16.34684372,-3.25158787,24.94408607,-27.18381500,-5.40719318,-11.48049831,-20.80558777,-4.13848925,21.21320534,-24.46477509,-4.86634493,-16.66710663,-24.46477509,-4.86634302,16.66710281,0.00000603,-0.00000903,30.00000000,-5.85270309,0.00000000,-29.42355919,0.00000978,0.00000000,-30.00000000,4.13849926,-4.13849020,-29.42355919,3.25160027,-4.86634970,-29.42355919,2.23974419,-5.40719748,-29.42355919,1.14181614,-5.74025059,-29.42355919,0.00000944,-5.85270786,-29.42355919,-1.14179730,-5.74024963,-29.42355919,-2.23972464,-5.40719604,-29.42355919,-3.25158024,-4.86634779,-29.42355919,-4.13847923,-4.13848829,-29.42355919,-4.86633825,-3.25158954,-29.42355919,-5.40718555,-2.23973346,-29.42355919,-5.74023771,-1.14180613,-29.42355919};
unsigned int bufPosLen = sizeof(posElems) / sizeof(posElems[0]);
unsigned int compCount = 3;
unsigned int elemCount = bufPosLen / compCount;
csRef bufPos = csRenderBuffer::CreateRenderBuffer5;
}

//factory wrapper
csRef wrap = psengine->GetEngine()->CreateMeshFactory(fact, “moonfact”);
if (wrap == 0)
Err::FatalError(“EuLOADER: failed to create factory wrapper!\n”);
return wrap;
}

Creating the terrain was quite a pain and it’s quite a long adventure so perhaps of some use to see the code for it too:

csRef EuLoader::GetTerrain() {
csRef meshWrapper = 0;
csRef factWrapper = 0;
csRef fact;
csRef state;
csRef typePlugin;

const char meshNameC[] = “Terrain”;
const char factNameC[] = “terrainF”;
char meshName[MAX_STR_LEN+1];
char factName[MAX_STR_LEN+1];
strncpy(meshName, meshNameC, strlen(meshNameC));
strncpy(factName, factNameC, strlen(factNameC));
meshName[strlen(meshNameC)]=’\0′;
factName[strlen(factNameC)]=’\0′;

csRef s = psengine->GetEngine()->FindSector(“Rotces”);
typePlugin = csLoadPluginCheck ( psengine->GetObjectRegistry(), TERRAIN_PLUGIN, false);

//get heightmap from EuCache via SMGData i.e. make sure it is available before creating empty sector…
char hmapfile[MAX_STR_LEN+1];
unsigned int lhmapfile = MAX_STR_LEN;
if (!psengine->SMG.GetHeightMapFile(factName, hmapfile, &lhmapfile)) {
Err::NonfatalError(“EuLoader: failed to find heightmap for requested terrain factory!\n”);
return meshWrapper;
}
hmapfile[lhmapfile] = ‘\0’;

//get materialmap from EuCache via SMGData
char hmatfile[MAX_STR_LEN+1];
unsigned int lhmatfile = MAX_STR_LEN;
if (!psengine->SMG.GetMaterialMapFile(factName, hmatfile, &lhmatfile)) {
Err::NonfatalError(“EuLoader: failed to find materialmap for requested terrain factory!\n”);
return meshWrapper;
}
hmatfile[lhmatfile] = ‘\0’;

//no need for base material in fact for terrain – there are the ones in the palette!
fact = typePlugin->NewFactory ();
csRef terrainFact = scfQueryInterface (fact);
//obtain needed renderer, collider and data feeder
//NB: those are all defaults because CS doesn’t really have a choice anyway.
csRef renderer = csLoadPluginCheck (psengine->GetObjectRegistry(), TERRAIN_RENDERER);
csRef collider = csLoadPluginCheck (psengine->GetObjectRegistry(), TERRAIN_COLLIDER);
csRef datafeeder = csLoadPluginCheck (psengine->GetObjectRegistry(), TERRAIN_DATAFEEDER_THREADED);
if (renderer == 0 || collider == 0 || datafeeder == 0) {
Err::NonfatalError(“EuLoader::GetFactory can’t find renderer, collider and/or data feeder for generating the Terrain!\n”);
return meshWrapper;
}

terrainFact->SetRenderer( renderer );
terrainFact->SetCollider( collider );
terrainFact->SetFeeder( datafeeder );

//set default cell – DEFAULT values!
//this is a “pseudo-cell” that serves as blueprint for any cells created by this factory
//NB: apparently by default there are NO cells created (?!)
csRef defaultCell (terrainFact->GetDefaultCell());
if (!defaultCell.IsValid()) {
//this should NOT happen!
Err::NonfatalError(“INVALID default cell in terrain factory!\n”);
return meshWrapper;
}
defaultCell->SetSize (csVector3 (1024.0f, 63.0f, 1024.0f));
defaultCell->SetGridWidth (257);
defaultCell->SetGridHeight (257);
defaultCell->SetMaterialMapWidth (512);
defaultCell->SetMaterialMapHeight (512);
defaultCell->SetMaterialPersistent (false);
//render properties
defaultCell->GetRenderProperties()->SetParameter(“block resolution”, “16”);
defaultCell->GetRenderProperties()->SetParameter(“splat distance”, “100”);
defaultCell->GetRenderProperties()->SetParameter(“lod splitcoeff”, “8”);
defaultCell->GetRenderProperties()->SetParameter(“splat render priority”, “object2”);
//heightmap and material map aka “feederproperties”
defaultCell->GetFeederProperties()->SetParameter(“smooth heightmap”, “yes”);
defaultCell->GetFeederProperties()->SetParameter(“heightmap source”, hmapfile); //?
defaultCell->GetFeederProperties()->SetParameter(“materialmap source”, hmatfile); //?

//create ONE actual cell in this factory
csRef cell = terrainFact->AddCell();
cell->SetName(“0”); //default, one single cell “0”
if (cell == 0) {
Err::NonfatalError(“EuLoader: GetFactory fails to create ONE cell for terrain!\n”);
return 0;
}
//position of this one cell
cell->SetPosition(csVector2(-511, -511));

//create the actual factory wrapper and register it with the engine
factWrapper = psengine->GetEngine()->CreateMeshFactory(fact, factName);
if (factWrapper == 0) {
Err::FatalError(“EuLOADER: failed to create Terrain factory wrapper!\n”);
return meshWrapper;
}

meshWrapper = psengine->GetEngine()->CreateMeshWrapper (factWrapper, meshName, s, csVector3 (0,0,0));
if (meshWrapper == 0) {
Err::NonfatalError(“EuLoader:GetMesh failed to create the requested mesh despite having its factory wrapper!\n”);
return meshWrapper;
}
//add material palette if this mesh has any!
uint32_t index = 1;
char matName[MAX_STR_LEN+1];
unsigned int len = MAX_STR_LEN;
csRefArray pal; //just in case there IS a material palette…
while (psengine->SMG.GetMaterialPalette(meshName, index, matName, &len)){
matName[len] = ‘\0’;
csRef m = GetMaterial(matName);
if (m == 0) {
Err::NonfatalError(“EuLoader: failed to retrieve material for material palette!\n”);
}
else
pal.Push(m);
index = index + 1;
len = MAX_STR_LEN; //reset this…
}
if (index > 1) {
//aka there IS a material palette
csRef mm = meshWrapper->GetMeshObject();
if (mm==0) {
Err::NonfatalError(“EuLoader: failed to obtain the mesh object out of the mesh wrapper!\n”);
}
else {
csRef syst = scfQueryInterface (mm);
if (syst == 0) {
Err::NonfatalError(“EuLoader: failed to get the terrainSystem to set the material palette on it!\n”);
}
else
syst->SetMaterialPalette(pal);
}
}

meshWrapper->SetZBufMode(CS_ZBUF_USE); //DO read so that the zbuffer gets updated…ugh;only write to the z-buffer but do not read
meshWrapper->GetMeshObject()->SetMixMode(CS_FX_COPY); //this should be default for most (?); see p321 (333) in cs manual re mixmodes and blending

meshWrapper->SetRenderPriority(psengine->GetEngine()->GetObjectRenderPriority());
psengine->GetEngine()->PrecacheMesh(meshWrapper); //hmm?

//initialize collision detection for this entity
csRef cdsys = csQueryRegistry (psengine->GetObjectRegistry());
csColliderHelper::InitializeCollisionWrapper(cdsys, meshWrapper);

return meshWrapper;

Putting some of the above together, there’s a more useful GetMesh that takes a numeric id (hence, a Euloran proper id since all objects in Eulora have a numeric id) and either finds or creates the thing (according to what EuCore says that thing should be):

csRef EuLoader::GetMesh( uint32_t id) {
csRef meshWrapper = 0;
//retrieve name as CS finds meshes by name
char meshName[MAX_STR_LEN+1];
unsigned int meshNameLen = MAX_STR_LEN;

if (!psengine->SMG.GetName(id, meshName, &meshNameLen))
return meshWrapper; //nothing to do as there’s not enough info for this object
meshName[meshNameLen] = ‘\0’;
//check if it exists already
meshWrapper = psengine->GetEngine()->FindMeshObject(meshName);
if (meshWrapper != 0)
return meshWrapper; //found it so return it

//not found so create it, if there is enough information for it
//will need: factory name, location (aka sectorID + position x,y,z)
char factName[MAX_STR_LEN+1];
unsigned int factNameLen = MAX_STR_LEN;
float x,y,z,rx,ry,rz;
uint32_t locID;
if (! (psengine->SMG.GetFactoryName(id, factName, &factNameLen)) ||
! (psengine->SMG.GetPos(id, &x, &y, &z, &rx, &ry, &rz)) ||
! (psengine->SMG.GetLocID(id, &locID)) ) {

Err::NonfatalError(“EuLoader: unknown factory name and/or location for requested mesh!”);
return meshWrapper;
}
factName[factNameLen] = ‘\0’;
//everything IS available, so retrieve/create factory
csRef factory = GetFactory(factName);
if (factory == 0) {
Err::NonfatalError(“EuLoader: failed to retrieve factory for the required entity!\n”);
return meshWrapper;
}
//retrieve/create sector for this too!
char sName[MAX_STR_LEN+1];
unsigned int slen = MAX_STR_LEN;
if (! (psengine->SMG.GetName(locID, sName, &slen))) {
Err::NonfatalError(“EuLoader: failed to retrieve sector name for the required entity!\n”);
return meshWrapper;
}
sName[slen] = ‘\0’;
csRef s = psengine->GetEngine()->FindSector(sName);
if (s == 0) {
//this should NOT happen as the sector should call/create contained entities, not the other way around.
Err::NonfatalError(“EuLoader: failed to find the sector for the required entity!\n”);
return meshWrapper;
}

meshWrapper = psengine->GetEngine()->CreateMeshWrapper (factory, meshName, s, csVector3 (x,y,z));
if (meshWrapper == 0) {
Err::NonfatalError(“EuLoader:GetMesh failed to create the requested mesh despite having its factory wrapper!\n”);
return meshWrapper;
}

//add material palette if this mesh has any!
uint32_t index = 1;
char matName[MAX_STR_LEN+1];
unsigned int len = MAX_STR_LEN;
csRefArray pal; //just in case there IS a material palette…
while (psengine->SMG.GetMaterialPalette(meshName, index, matName, &len)){
matName[len] = ‘\0’;
csRef m = GetMaterial(matName);
if (m == 0) {
Err::NonfatalError(“EuLoader: failed to retrieve material for material palette!\n”);
}
else
pal.Push(m);
index = index + 1;
len = MAX_STR_LEN; //reset this…
}
if (index > 1) {
//aka there IS a material palette
csRef mm = meshWrapper->GetMeshObject();
if (mm==0) {
Err::NonfatalError(“EuLoader: failed to obtain the mesh object out of the mesh wrapper!\n”);
}
else {
csRef syst = scfQueryInterface (mm);
if (syst == 0) {
Err::NonfatalError(“EuLoader: failed to get the terrainSystem to set the material palette on it!\n”);
}
else
syst->SetMaterialPalette(pal);
}
}

//TO DO: to check that all mesh entities have indeed zbuf_use?
//NOPE, not all: particles have cs_zbuf_test
//the zbufmode depends on the type of the mesh, which is in turn given by the …factory type…
int mtype = psengine->SMG.GetFactoryType(factName);
switch (mtype) {
case FACTORY_PARTICLES: {
meshWrapper->SetZBufMode(CS_ZBUF_TEST);
meshWrapper->GetMeshObject()->SetMixMode(CS_FX_ADD);
// meshWrapper->SetRenderPriority(psengine->GetEngine()->GetObjectRenderPriority());
meshWrapper->SetRenderPriority(psengine->GetEngine()->GetAlphaRenderPriority());

} break;
case FACTORY_TERRAIN: {
meshWrapper->SetZBufMode(CS_ZBUF_USE);
meshWrapper->GetMeshObject()->SetMixMode(CS_FX_COPY);
meshWrapper->SetRenderPriority(psengine->GetEngine()->GetWallRenderPriority());
} break;
default: {
meshWrapper->SetZBufMode(CS_ZBUF_USE); //DO read so that the zbuffer gets updated…ugh;only write to the z-buffer but do not read
meshWrapper->GetMeshObject()->SetMixMode(CS_FX_COPY); //this should be default for most (?); see p321 (333) in cs manual re mixmodes and blending
meshWrapper->SetRenderPriority(psengine->GetEngine()->GetObjectRenderPriority());
}
}
//ALL mesh entities are objects so set them with object priority for rendering;
//TO DO: to check this since “entities” include walls and doors and the like?
//i.e. might need other meaningful subcategories and decide rendering priority on that; hm.
// meshWrapper->SetRenderPriority(psengine->GetEngine()->GetRenderPriority(“wall”));
psengine->GetEngine()->PrecacheMesh(meshWrapper); //hmm?

//initialize collision detection for this entity
csRef cdsys = csQueryRegistry (psengine->GetObjectRegistry());
csColliderHelper::InitializeCollisionWrapper(cdsys, meshWrapper);

return meshWrapper;

}

For some really tedious stuff, here’s the creation of Foxy herself, hence a Cal3D object:

csRef EuLoader::GetCal3d( char *name, iSector *s, float x, float y, float z ) {
csRef meshWrapper = 0;
//check if it exists already
meshWrapper = psengine->GetEngine()->FindMeshObject(name);
if (meshWrapper != 0)
return meshWrapper; //found it so return it

//check sector is not null since CS REQUIRES a non-null sector to even create a mesh, ugh.
if (!s)
return meshWrapper;

//not found the mesh, so need the factory to create it…
csRef factWrapper = 0;
factWrapper = psengine->GetEngine()->FindMeshFactory(name);

if (!factWrapper) {
//factory does not yet exist, so create it.
csRef fact;
csRef state;
csRef typePlugin;

typePlugin = csLoadPluginCheck ( psengine->GetObjectRegistry(), CAL3D_PLUGIN, false);
if (typePlugin == 0) {
Err::NonfatalError(“EuLoader failed to load SprCal3D Factory PLUGIN TYPE\n”);
return meshWrapper;
}
fact = typePlugin->NewFactory ();

//create the actual factory wrapper and register it with the engine
factWrapper = psengine->GetEngine()->CreateMeshFactory(fact, name);
if (factWrapper == 0) {
Err::FatalError(“EuLOADER: failed to create Cal3D factory wrapper!\n”);
return meshWrapper;
}
} //by this point, factWrapper was either found OR created successfully.

//now get the specific factory and state for Cal3D to set everything
csRef state3d = scfQueryInterface (factWrapper->GetMeshObjectFactory());
if (state3d == 0) {
Err::NonfatalError(“EuLoader failed to load factory state as iSpriteCal2DFactoryState\n”);
return meshWrapper;
}
//test for now – create girl factory
//this IS DONE in CS’s loader – does each sprcal3d factory actually NEED to create this “dummy” ?
if (!state3d->Create(“dummy”)) {
Err::NonfatalError(“EuLoader: failed to create dummy for sprcal3d factory\n”);
return meshWrapper;
}
//parameters for sprCal3dFactory state – to take them from SMGData! TO DO TO CHANGE
float scale = 1.0;
float ax, ay, az, angle;
csVector3 translation(0.0,0.0,0.0);
int flags = LOADER_FLIP_WINDING; //wtf IS this??
bool rotXAxis = true; //default apparently
bool flipTextures = false; //default apparently
if (rotXAxis)
flags = flags | LOADER_ROTATE_X_AXIS;
else
flags = flags & (~LOADER_ROTATE_X_AXIS); //ugh.
if (flipTextures)
flags = flags | LOADER_INVERT_V_COORD;
else
flags = flags & (~LOADER_INVERT_V_COORD);

//skeleton
char skelFile[] = “/this/clientcache/girl_skel.csf”;
if (! state3d->LoadCoreSkeleton(psengine->GetVFS(), skelFile) ){
Err::NonfatalError(“Failed to load the core skeleton file girl_skel.csf\n”);
return meshWrapper;
}
char idleFile[] = “/this/clientcache/girl_idle.caf”;
char idleName[] = “idle”;
char walkFile[] = “/this/clientcache/girl_idle.caf”;
char walkName[] = “walk”;
int itype = iSpriteCal3DState::C3D_ANIM_TYPE_IDLE;
int wtype = iSpriteCal3DState::C3D_ANIM_TYPE_TRAVEL;

//see p 287/644 in cs manual
float base_vel = 0; //”speed of translation which should be used when the animation is played alone”
float min_vel = 0; //min and max velocities are used by the blender to achieve specific, desired velocities
float max_vel = 0;
int max_interval = 30; //max_random – those give the interval (in seconds) for idle override actions
int min_interval = 10; //min_random
int idle_pct = 0; //probability (%) of this action being the override action
bool lock = false; //??

int animID = state3d->LoadCoreAnimation(psengine->GetVFS(), idleFile,
idleName, itype, base_vel,
min_vel, max_vel, min_interval, max_interval,
idle_pct, lock, flags);
if (animID < 0) { Err::NonfatalError("EuLoader: failed to load core animation\n"); return meshWrapper; } base_vel = 2; max_vel = 3; animID = state3d->LoadCoreAnimation(psengine->GetVFS(), walkFile,
walkName, wtype, base_vel,
min_vel, max_vel, min_interval, max_interval,
idle_pct, lock, flags);
if (animID < 0) { Err::NonfatalError("EuLoader: failed to load core animation\n"); return meshWrapper; } //meshes aka "attachable parts of the model" //head char mfile[] = "/this/clientcache/girl_head.cmf"; char hname[] = "Head"; char mname[] = "/this/clientcache/fskin.dds"; // char mname[] = "fskin.dds"; csRef mtex = GetTexture(mname); //to create/get a BASIC material with this name rather than all the shader mess.
csRef mw = GetMaterial(mname); //after the GetTexture call, this should always end up with the basic wrapper-material on the corresponding texture… such is CS.

if (mw == 0) {
Err::NonfatalError(“FAILED to obtain material fskin.dds\n”);
return meshWrapper;
}
bool attach = true; //default value
state3d->AddCoreMaterial(mw); //done via LoadMaterialTag in cs/plugins/mesh/sprcal3d/persist/sprcal3dldr.cpp
if (state3d->LoadCoreMesh(psengine->GetVFS(), mfile, hname, attach, mw, flags) < 0) { Err::NonfatalError("FAILED to loadcoremesh\n"); return meshWrapper; } //hair char ghrfile[] = "/this/clientcache/girl_hair.cmf"; char ghrname[] = "Hair"; if (state3d->LoadCoreMesh(psengine->GetVFS(), ghrfile, ghrname, attach, mw, flags) < 0) { Err::NonfatalError("FAILED to loadcoremesh\n"); return meshWrapper; } //arm(s) char glalfile[] = "/this/clientcache/girl_arm.cmf"; char glalname[] = "Arm"; if (state3d->LoadCoreMesh(psengine->GetVFS(), glalfile, glalname, attach, mw, flags) < 0) { Err::NonfatalError("FAILED to loadcoremesh\n"); return meshWrapper; } //hand(s) char ghhfile[] = "/this/clientcache/girl_hand.cmf"; char ghhname[] = "Hand"; if (state3d->LoadCoreMesh(psengine->GetVFS(), ghhfile, ghhname, attach, mw, flags) < 0) { Err::NonfatalError("FAILED to loadcoremesh\n"); return meshWrapper; } //torso char gtfile[] = "/this/clientcache/girl_torso.cmf"; char gtname[] = "Torso"; if (state3d->LoadCoreMesh(psengine->GetVFS(), gtfile, gtname, attach, mw, flags) < 0) { Err::NonfatalError("FAILED to loadcoremesh\n"); return meshWrapper; } //legs char glfile[] = "/this/clientcache/girl_legs.cmf"; char glname[] = "Legs"; if (state3d->LoadCoreMesh(psengine->GetVFS(), glfile, glname, attach, mw, flags) < 0) { Err::NonfatalError("FAILED to loadcoremesh\n"); return meshWrapper; } //foot char gffile[] = "/this/clientcache/girl_foot.cmf"; char gffname[] = "Foot"; if (state3d->LoadCoreMesh(psengine->GetVFS(), gffile, gffname, attach, mw, flags) < 0) { Err::NonfatalError("FAILED to loadcoremesh\n"); return meshWrapper; } //default values - hardtransform ax = 0.0; ay = 1.0; //this turns it to face away from the camera/viewer as it were az = 0.0; angle = 180.0; csMatrix3 rotation(ax,ay,az,angle*TWO_PI/360); csReversibleTransform rt(rotation,translation); factWrapper->GetMeshObjectFactory()->HardTransform(rt);

angle = 90.0;
ay = 0.0;
ax = -1.0;
csMatrix3 rotation1(ax,ay,az,angle*TWO_PI/360);
csReversibleTransform rt1(rotation1,translation);
factWrapper->GetMeshObjectFactory()->HardTransform(rt1);

//rescale if needed
if (scale != 0)
state3d->RescaleFactory(scale); //this ALSO calls the calculateallboneboundingboxes
else
state3d->CalculateAllBoneBoundingBoxes();
// Wrapup cal3d initialization
state3d->BindMaterials();

//and now create and register the mesh itself
meshWrapper = psengine->GetEngine()->CreateMeshWrapper (factWrapper, name, s, csVector3 (x,y,z));
if (meshWrapper == 0) {
Err::NonfatalError(“EuLoader:GetMesh failed to create the requested mesh despite having its factory wrapper!\n”);
return meshWrapper;
}

meshWrapper->SetZBufMode(CS_ZBUF_USE); //DO read so that the zbuffer gets updated…ugh;only write to the z-buffer but do not read
meshWrapper->GetMeshObject()->SetMixMode(CS_FX_COPY); //this should be default for most (?); see p321 (333) in cs manual re mixmodes and blending
meshWrapper->SetRenderPriority(psengine->GetEngine()->GetObjectRenderPriority());

psengine->GetEngine()->PrecacheMesh(meshWrapper); //hmm?

//initialize collision detection for this entity
csRef cdsys = csQueryRegistry (psengine->GetObjectRegistry());
csColliderHelper::InitializeCollisionWrapper(cdsys, meshWrapper);

return meshWrapper;
}

Finally, the sector itself with lights and then various tests, some still in, some commented out (although the working code is still there otherwise):

bool EuLoader::LoadSector(uint32_t id) {
/*****retrieve name of this sector and check if it is already loaded*****/
char sname[MAX_STR_LEN+1];
unsigned int lsname = MAX_STR_LEN;

//get name of this sector and null-terminate it
if (!psengine->SMG.GetName(id, sname, &lsname))
return false;
sname[lsname] = ‘\0’;

//check if sector exists
iSector *s = psengine->GetEngine()->FindSector(sname);
if (s != NULL) {
return true; //sector already loaded
}

/****create the sector as an empty entity with default overall values****/
s = psengine->GetEngine()->CreateSector(sname);
if (s == NULL) {
//this should never happen.
Err::NonfatalError(“EuLoader::LoadSector failed to create the requested sector.”);
return false; //failed to create a sector
}

//set the visibility culler; NB: smgdata provides either defaults or fresh data
s->SetVisibilityCullerPlugin(DEFAULT_CULLER); //default culler defined in smgdata.h
float r,g,b;
//set the ambient light
if (psengine->SMG.GetAmbient(id, &r, &g, &b))
s->SetDynamicAmbientLight(csColor(r,g,b));
//set the fog, if any – not really, this was/is only in water (?)
// float density;
// if (psengine->SMG.GetFog(id, &r, &g, &b, &density))
// s->SetFog(density, csColor(r,g,b));

//load the lights, if any
uint32_t lindex = 1;
float radius, x, y, z;
char lightName[MAX_STR_LEN+1];
unsigned int llen = MAX_STR_LEN;
bool dynamic;
csLightDynamicType ltype;

while (psengine->SMG.GetLight(id, lindex, lightName, &llen, &x, &y, &z, &radius, &r, &g, &b, &dynamic)) {
lightName[llen] = ‘\0’;
if (dynamic)
ltype = CS_LIGHT_DYNAMICTYPE_DYNAMIC;
else
ltype = CS_LIGHT_DYNAMICTYPE_STATIC;
csRef light = psengine->GetEngine()->CreateLight(lightName, csVector3(x, y, z), radius, csColor(r,g,b), ltype);
if (!light)
Err::NonfatalError(“EuLoader::LoadSector failed to create light”);
else {
s->GetLights()->Add(light);
}
lindex = lindex + 1;
}

//sky
//using GetMaterial, GetTexture, GetShader above

char skyfactoryname[] = “sky.png.fact”;
char skyobjname[] = “skyobj”;
// csRef skyFactory = GetFactory(skyfactoryname);
if (!GetSky()) { //separate for now because generated sphere aka no direct pixels, texels etc.
// if (skyFactory==0) {
Err::NonfatalError(“EuLoader::LoadSector failed to obtain the sky factory\n”);
return false;
}
else printf (“CHECK: EuLoader got the sky factory fine\n”);

//TO DO : this should create all known objects in the sector
//the moon
/* csRef moonwrap = GetMesh(901);
if (moonwrap == 0) {
Err::NonfatalError(“NO MOON!!!\n”);
return false;
}
*/

//the terrain
csRef twrap = GetTerrain();
if (twrap == 0) {
Err::NonfatalError(“NO TERRAIN!!!\n”);
return false;
}

/************create particle systems (effects) *********/
// csRef fire = GetFire();
csRef fountain = GetFountain();
// csRef rain = GetRain();
// csRef globe = GetGlobe();

/************create the Water***************/

/************create the Portals*************/
return true;
}

There is of course more to the prototoype/test code and otherwise changes and excisions to legacy code but comments are helpfully in place everywhere and so I don’t feel the need to repeat them here. Despite those added comments and despite the fact that a lot of now-unused code was commented out rather than deleted at this stage, this prototype client is still thousands LOC shorter than the legacy client so there’s some satisfaction to it all at least, that’s the most I can say on it currently. Feel free to ask your questions in the comments box below, as usual.

  1. Yeah, I’m writing to my older self as it’s way more useful than writing to my younger self. You should try it too.[]
  2. See src/client/euloader.h and .cpp for the prototype loaders’ code + src/client/psengine.cpp for its use.[]
  3. To do the same for different plugins, you need just to replace TERRAIN_PLUGIN with whichever other plugin you want from the list above.[]
  4. To quote Eminescu’s fitting words here: “O racla mare-i lumea. Stelele-s cuie batute-n ea si soarele-i fereastra la temnita vietii.” (A large shrine is this world. The stars are nails through it and the sun is the window to life’s gaol.) []
  5. size_t) elemCount, CS_BUF_STATIC, CS_BUFCOMP_FLOAT, compCount);
    bufPos->CopyInto(posElems, elemCount); //this is the only available method to copy apparently the data from memory/pointer and into the buffer….
    state->AddRenderBuffer(“position”, bufPos);

    //renderbuffers: normal
    //TO DO TO CHANGE retrieve the contents of the buffer from SMGData
    const float normElems[] = {-0.81731617,0.16257210,0.55275124,-0.69649345,0.13852352,0.70403147,-0.71013522,0.00000000,0.70403147,-0.83333844,0.00000000,0.55275124,-0.69649345,0.13852352,-0.70403147,-0.81731617,0.16257210,-0.55275124,-0.83333844,0.00000000,-0.55275124,-0.71013522,0.00000000,-0.70403147,-0.90697956,0.18039490,0.38053530,-0.92474133,0.00000000,0.38053530,-0.54899746,0.10919522,-0.82863855,-0.55977052,0.00000000,-0.82863855,-0.96215707,0.19138157,0.19391461,-0.98098695,0.00000000,0.19391461,-0.38041323,0.07565539,-0.92168951,-0.38785973,0.00000000,-0.92168951,-0.98077333,0.19507432,0.00000000,-1.00000000,0.00000000,0.00000000,-0.38041323,0.07565539,0.92168951,-0.19708854,0.03918577,0.97958314,-0.20096439,0.00000000,0.97958314,-0.38785973,0.00000000,0.92168951,-0.19708854,0.03918577,-0.97958314,-0.20096439,0.00000000,-0.97958314,-0.96215707,0.19138157,-0.19391461,-0.98098695,0.00000000,-0.19391461,-0.54899746,0.10919522,0.82863855,-0.55977052,0.00000000,0.82863855,-0.90697956,0.18039490,-0.38053530,-0.92474133,0.00000000,-0.38053530,-0.35834834,0.14841151,-0.92168951,-0.51713616,0.21420942,-0.82863855,-0.54899746,0.10919522,-0.82863855,-0.38041323,0.07565539,-0.92168951,-0.92385632,0.38267159,0.00000000,-0.90633869,0.37540817,0.19391461,-0.35834834,0.14841151,0.92168951,-0.18564409,0.07690664,0.97958314,-0.18564409,0.07690664,-0.97958314,-0.19708854,0.03918577,-0.97958314,-0.90633869,0.37540817,-0.19391461,-0.51713616,0.21420942,0.82863855,-0.85436565,0.35389262,-0.38053530,-0.65605640,0.27173680,0.70403147,-0.76989043,0.31888792,-0.55275124,-0.76989043,0.31888792,0.55275124,-0.65605640,0.27173680,-0.70403147,-0.85436565,0.35386211,0.38053530,-0.51713616,0.21420942,-0.82863855,-0.59044158,0.39451277,-0.70403147,-0.69289225,0.46296579,-0.55275124,-0.76989043,0.31888792,-0.55275124,-0.65605640,0.27173680,-0.70403147,-0.76891387,0.51374859,0.38053530,-0.69289225,0.46296579,0.55275124,-0.76989043,0.31888792,0.55275124,-0.85436565,0.35386211,0.38053530,-0.46540728,0.31098360,-0.82863855,-0.81566823,0.54499954,0.19391461,-0.90633869,0.37540817,0.19391461,-0.32248908,0.21549119,-0.92168951,-0.83144629,0.55555892,0.00000000,-0.92385632,0.38267159,0.00000000,-0.32248908,0.21549119,0.92168951,-0.16708884,0.11163671,0.97958314,-0.16708884,0.11163671,-0.97958314,-0.81566823,0.54499954,-0.19391461,-0.90633869,0.37540817,-0.19391461,-0.46540728,0.31098360,0.82863855,-0.51713616,0.21420942,0.82863855,-0.76891387,0.51374859,-0.38053530,-0.85436565,0.35389262,-0.38053530,-0.59044158,0.39451277,0.70403147,-0.65605640,0.27173680,0.70403147,-0.65388960,0.65388960,-0.38053530,-0.69365519,0.69365519,-0.19391461,-0.50212103,0.50212103,0.70403147,-0.39579454,0.39579454,0.82863855,-0.58925140,0.58925140,-0.55275124,-0.58925140,0.58925140,0.55275124,-0.50212103,0.50212103,-0.70403147,-0.65388960,0.65388960,0.38053530,-0.39579454,0.39579454,-0.82863855,-0.69365519,0.69365519,0.19391461,-0.27426985,0.27426985,-0.92168951,-0.70708334,0.70708334,0.00000000,-0.27426985,0.27426985,0.92168951,-0.14209418,0.14209418,0.97958314,-0.14209418,0.14209418,-0.97958314,-0.21549119,0.32248908,-0.92168951,-0.31098360,0.46540728,-0.82863855,-0.55555892,0.83144629,0.00000000,-0.54499954,0.81566823,0.19391461,-0.21549119,0.32248908,0.92168951,-0.11163671,0.16708884,0.97958314,-0.11163671,0.16708884,-0.97958314,-0.54499954,0.81566823,-0.19391461,-0.31098360,0.46540728,0.82863855,-0.51374859,0.76891387,-0.38053530,-0.39451277,0.59044158,0.70403147,-0.46296579,0.69289225,-0.55275124,-0.46296579,0.69289225,0.55275124,-0.39451277,0.59044158,-0.70403147,-0.51374859,0.76891387,0.38053530,-0.31888792,0.76989043,0.55275124,-0.27173680,0.65605640,0.70403147,-0.27173680,0.65605640,-0.70403147,-0.31888792,0.76989043,-0.55275124,-0.35386211,0.85436565,0.38053530,-0.21420942,0.51713616,-0.82863855,-0.37540817,0.90633869,0.19391461,-0.14841151,0.35834834,-0.92168951,-0.38267159,0.92385632,0.00000000,-0.14841151,0.35834834,0.92168951,-0.07690664,0.18564409,0.97958314,-0.07690664,0.18564409,-0.97958314,-0.37540817,0.90633869,-0.19391461,-0.21420942,0.51713616,0.82863855,-0.35386211,0.85436565,-0.38053530,-0.19138157,0.96215707,-0.19391461,-0.19507432,0.98077333,0.00000000,-0.10919522,0.54899746,0.82863855,-0.07565539,0.38041323,0.92168951,-0.18039490,0.90697956,-0.38053530,-0.13852352,0.69649345,0.70403147,-0.16257210,0.81731617,-0.55275124,-0.16257210,0.81731617,0.55275124,-0.13852352,0.69649345,-0.70403147,-0.18039490,0.90697956,0.38053530,-0.10919522,0.54899746,-0.82863855,-0.19138157,0.96215707,0.19391461,-0.07565539,0.38041323,-0.92168951,-0.03918577,0.19708854,0.97958314,-0.03918577,0.19708854,-0.97958314,0.00000000,0.55977052,-0.82863855,0.00000000,0.71013522,-0.70403147,0.00000000,0.98098695,0.19391461,0.00000000,0.92474133,0.38053530,0.00000000,0.38785973,-0.92168951,0.00000000,1.00000000,0.00000000,0.00000000,0.38785973,0.92168951,0.00000000,0.20096439,0.97958314,0.00000000,0.20096439,-0.97958314,0.00000000,0.98098695,-0.19391461,0.00000000,0.55977052,0.82863855,0.00000000,0.92474133,-0.38053530,0.00000000,0.71013522,0.70403147,0.00000000,0.83333844,-0.55275124,0.00000000,0.83333844,0.55275124,0.16257210,0.81731617,-0.55275124,0.18039490,0.90697956,-0.38053530,0.16257210,0.81731617,0.55275124,0.13852352,0.69649345,0.70403147,0.13852352,0.69649345,-0.70403147,0.18039490,0.90697956,0.38053530,0.10919522,0.54899746,-0.82863855,0.19138157,0.96215707,0.19391461,0.07565539,0.38041323,-0.92168951,0.19507432,0.98077333,0.00000000,0.07565539,0.38041323,0.92168951,0.03918577,0.19708854,0.97958314,0.03918577,0.19708854,-0.97958314,0.19138157,0.96215707,-0.19391461,0.10919522,0.54899746,0.82863855,0.07690664,0.18564409,-0.97958314,0.14841151,0.35834834,-0.92168951,0.37540817,0.90633869,-0.19391461,0.38267159,0.92385632,0.00000000,0.21420942,0.51713616,0.82863855,0.14841151,0.35834834,0.92168951,0.35389262,0.85436565,-0.38053530,0.27173680,0.65605640,0.70403147,0.31888792,0.76989043,-0.55275124,0.31888792,0.76989043,0.55275124,0.27173680,0.65605640,-0.70403147,0.35389262,0.85436565,0.38053530,0.21420942,0.51713616,-0.82863855,0.37540817,0.90633869,0.19391461,0.07687613,0.18564409,0.97958314,0.31098360,0.46540728,-0.82863855,0.39451277,0.59044158,-0.70403147,0.54499954,0.81566823,0.19391461,0.51374859,0.76891387,0.38053530,0.21549119,0.32248908,-0.92168951,0.55555892,0.83144629,0.00000000,0.21549119,0.32248908,0.92168951,0.11163671,0.16708884,0.97958314,0.11163671,0.16708884,-0.97958314,0.54499954,0.81566823,-0.19391461,0.31098360,0.46540728,0.82863855,0.51374859,0.76891387,-0.38053530,0.39451277,0.59044158,0.70403147,0.46296579,0.69289225,-0.55275124,0.46296579,0.69289225,0.55275124,0.58925140,0.58925140,-0.55275124,0.65388960,0.65388960,-0.38053530,0.58925140,0.58925140,0.55275124,0.50212103,0.50212103,0.70403147,0.50212103,0.50212103,-0.70403147,0.65388960,0.65388960,0.38053530,0.39579454,0.39579454,-0.82863855,0.69365519,0.69365519,0.19391461,0.27426985,0.27426985,-0.92168951,0.70708334,0.70708334,0.00000000,0.27426985,0.27426985,0.92168951,0.14209418,0.14209418,0.97958314,0.14209418,0.14209418,-0.97958314,0.69365519,0.69365519,-0.19391461,0.39579454,0.39579454,0.82863855,0.16708884,0.11163671,-0.97958314,0.32248908,0.21549119,-0.92168951,0.81566823,0.54499954,-0.19391461,0.83144629,0.55555892,0.00000000,0.46540728,0.31098360,0.82863855,0.32248908,0.21549119,0.92168951,0.76891387,0.51374859,-0.38053530,0.59044158,0.39451277,0.70403147,0.69289225,0.46296579,-0.55275124,0.69289225,0.46296579,0.55275124,0.59044158,0.39451277,-0.70403147,0.76891387,0.51374859,0.38053530,0.46540728,0.31098360,-0.82863855,0.81566823,0.54499954,0.19391461,0.16708884,0.11163671,0.97958314,0.51713616,0.21420942,-0.82863855,0.65605640,0.27173680,-0.70403147,0.90633869,0.37540817,0.19391461,0.85436565,0.35386211,0.38053530,0.35834834,0.14841151,-0.92168951,0.92385632,0.38267159,0.00000000,0.35834834,0.14841151,0.92168951,0.18564409,0.07690664,0.97958314,0.18564409,0.07690664,-0.97958314,0.90633869,0.37540817,-0.19391461,0.51713616,0.21420942,0.82863855,0.85436565,0.35386211,-0.38053530,0.65605640,0.27173680,0.70403147,0.76989043,0.31888792,-0.55275124,0.76989043,0.31888792,0.55275124,0.69649345,0.13852352,0.70403147,0.54899746,0.10919522,0.82863855,0.81731617,0.16257210,-0.55275124,0.90697956,0.18039490,-0.38053530,0.81731617,0.16257210,0.55275124,0.69649345,0.13852352,-0.70403147,0.90697956,0.18039490,0.38053530,0.54899746,0.10919522,-0.82863855,0.96215707,0.19138157,0.19391461,0.38041323,0.07565539,-0.92168951,0.98077333,0.19507432,0.00000000,0.38041323,0.07565539,0.92168951,0.19708854,0.03918577,0.97958314,0.19708854,0.03918577,-0.97958314,0.96215707,0.19138157,-0.19391461,1.00000000,0.00000000,0.00000000,0.98098695,0.00000000,0.19391461,0.38785973,0.00000000,0.92168951,0.20096439,0.00000000,0.97958314,0.20096439,0.00000000,-0.97958314,0.38785973,0.00000000,-0.92168951,0.98098695,0.00000000,-0.19391461,0.55977052,0.00000000,0.82863855,0.92474133,0.00000000,-0.38053530,0.71013522,0.00000000,0.70403147,0.83333844,0.00000000,-0.55275124,0.83333844,0.00000000,0.55275124,0.71013522,0.00000000,-0.70403147,0.92474133,0.00000000,0.38053530,0.55977052,0.00000000,-0.82863855,0.69649345,-0.13852352,-0.70403147,0.81731617,-0.16257210,-0.55275124,0.90697956,-0.18039490,0.38053530,0.81731617,-0.16257210,0.55275124,0.54899746,-0.10919522,-0.82863855,0.96215707,-0.19138157,0.19391461,0.38041323,-0.07565539,-0.92168951,0.98077333,-0.19507432,0.00000000,0.38041323,-0.07565539,0.92168951,0.19708854,-0.03918577,0.97958314,0.19708854,-0.03918577,-0.97958314,0.96215707,-0.19138157,-0.19391461,0.54899746,-0.10919522,0.82863855,0.90697956,-0.18039490,-0.38053530,0.69649345,-0.13852352,0.70403147,0.85436565,-0.35386211,-0.38053530,0.90633869,-0.37540817,-0.19391461,0.65605640,-0.27173680,0.70403147,0.51713616,-0.21420942,0.82863855,0.76989043,-0.31888792,-0.55275124,0.76989043,-0.31888792,0.55275124,0.65605640,-0.27173680,-0.70403147,0.85436565,-0.35386211,0.38053530,0.51713616,-0.21420942,-0.82863855,0.90633869,-0.37540817,0.19391461,0.35834834,-0.14841151,-0.92168951,0.92385632,-0.38267159,0.00000000,0.35834834,-0.14841151,0.92168951,0.18564409,-0.07690664,0.97958314,0.18564409,-0.07690664,-0.97958314,0.32248908,-0.21549119,-0.92168951,0.46540728,-0.31098360,-0.82863855,0.83144629,-0.55555892,0.00000000,0.81566823,-0.54499954,0.19391461,0.32248908,-0.21549119,0.92168951,0.16708884,-0.11163671,0.97958314,0.16708884,-0.11163671,-0.97958314,0.81566823,-0.54499954,-0.19391461,0.46540728,-0.31098360,0.82863855,0.76891387,-0.51374859,-0.38053530,0.59044158,-0.39451277,0.70403147,0.69289225,-0.46296579,-0.55275124,0.69289225,-0.46296579,0.55275124,0.59044158,-0.39451277,-0.70403147,0.76891387,-0.51374859,0.38053530,0.46540728,-0.31098360,-0.82863855,0.50212103,-0.50212103,-0.70403147,0.58925140,-0.58925140,-0.55275124,0.65388960,-0.65388960,0.38053530,0.58925140,-0.58925140,0.55275124,0.39579454,-0.39579454,-0.82863855,0.69365519,-0.69365519,0.19391461,0.27426985,-0.27426985,-0.92168951,0.32248908,-0.21549119,-0.92168951,0.70708334,-0.70708334,0.00000000,0.27426985,-0.27426985,0.92168951,0.14209418,-0.14209418,0.97958314,0.14209418,-0.14209418,-0.97958314,0.16708884,-0.11163671,-0.97958314,0.69365519,-0.69365519,-0.19391461,0.39579454,-0.39579454,0.82863855,0.65388960,-0.65388960,-0.38053530,0.50212103,-0.50212103,0.70403147,0.51374859,-0.76891387,-0.38053530,0.54499954,-0.81566823,-0.19391461,0.39451277,-0.59044158,0.70403147,0.31098360,-0.46540728,0.82863855,0.46296579,-0.69289225,-0.55275124,0.46296579,-0.69289225,0.55275124,0.39451277,-0.59044158,-0.70403147,0.51374859,-0.76891387,0.38053530,0.31098360,-0.46540728,-0.82863855,0.54499954,-0.81566823,0.19391461,0.21549119,-0.32248908,-0.92168951,0.55555892,-0.83144629,0.00000000,0.21549119,-0.32248908,0.92168951,0.11163671,-0.16708884,0.97958314,0.11163671,-0.16708884,-0.97958314,0.14841151,-0.35834834,-0.92168951,0.21420942,-0.51713616,-0.82863855,0.38267159,-0.92385632,0.00000000,0.37540817,-0.90633869,0.19391461,0.14841151,-0.35834834,0.92168951,0.07687613,-0.18564409,0.97958314,0.07690664,-0.18564409,-0.97958314,0.37540817,-0.90633869,-0.19391461,0.21420942,-0.51713616,0.82863855,0.35386211,-0.85436565,-0.38053530,0.27173680,-0.65605640,0.70403147,0.31888792,-0.76989043,-0.55275124,0.31888792,-0.76989043,0.55275124,0.27173680,-0.65605640,-0.70403147,0.35386211,-0.85436565,0.38053530,0.13852352,-0.69649345,-0.70403147,0.16257210,-0.81731617,-0.55275124,0.18039490,-0.90697956,0.38053530,0.16257210,-0.81731617,0.55275124,0.10919522,-0.54899746,-0.82863855,0.19138157,-0.96215707,0.19391461,0.07565539,-0.38041323,-0.92168951,0.19507432,-0.98077333,0.00000000,0.07565539,-0.38041323,0.92168951,0.03918577,-0.19708854,0.97958314,0.03918577,-0.19708854,-0.97958314,0.19138157,-0.96215707,-0.19391461,0.10919522,-0.54899746,0.82863855,0.18039490,-0.90697956,-0.38053530,0.13852352,-0.69649345,0.70403147,0.00000000,-0.55977052,0.82863855,0.00000000,-0.38785973,0.92168951,0.00000000,-0.92474133,-0.38053530,0.00000000,-0.98098695,-0.19391461,0.00000000,-0.71013522,0.70403147,0.00000000,-0.83333844,-0.55275124,0.00000000,-0.83333844,0.55275124,0.00000000,-0.71013522,-0.70403147,0.00000000,-0.92474133,0.38053530,0.00000000,-0.55977052,-0.82863855,0.00000000,-0.98098695,0.19391461,0.00000000,-0.38785973,-0.92168951,0.00000000,-0.99996948,0.00000000,0.00000000,-0.20096439,0.97958314,0.00000000,-0.20096439,-0.97958314,-0.19138157,-0.96215707,0.19391461,-0.18039490,-0.90697956,0.38053530,-0.07565539,-0.38041323,-0.92168951,-0.10919522,-0.54899746,-0.82863855,-0.19507432,-0.98077333,0.00000000,-0.07565539,-0.38041323,0.92168951,-0.03918577,-0.19708854,0.97958314,-0.03918577,-0.19708854,-0.97958314,-0.19138157,-0.96215707,-0.19391461,-0.10919522,-0.54899746,0.82863855,-0.18039490,-0.90697956,-0.38053530,-0.13852352,-0.69649345,0.70403147,-0.16257210,-0.81731617,-0.55275124,-0.16257210,-0.81731617,0.55275124,-0.13852352,-0.69649345,-0.70403147,-0.31888792,-0.76989043,-0.55275124,-0.35389262,-0.85436565,-0.38053530,-0.31888792,-0.76989043,0.55275124,-0.27173680,-0.65605640,0.70403147,-0.27173680,-0.65605640,-0.70403147,-0.35389262,-0.85436565,0.38053530,-0.21420942,-0.51713616,-0.82863855,-0.37540817,-0.90633869,0.19391461,-0.14841151,-0.35834834,-0.92168951,-0.38267159,-0.92385632,0.00000000,-0.14841151,-0.35834834,0.92168951,-0.07690664,-0.18564409,0.97958314,-0.07690664,-0.18564409,-0.97958314,-0.37540817,-0.90633869,-0.19391461,-0.21420942,-0.51713616,0.82863855,-0.11163671,-0.16708884,-0.97958314,-0.21549119,-0.32248908,-0.92168951,-0.54499954,-0.81566823,-0.19391461,-0.55555892,-0.83144629,0.00000000,-0.31098360,-0.46540728,0.82863855,-0.21549119,-0.32248908,0.92168951,-0.51374859,-0.76891387,-0.38053530,-0.39451277,-0.59044158,0.70403147,-0.46296579,-0.69289225,-0.55275124,-0.46296579,-0.69289225,0.55275124,-0.39451277,-0.59044158,-0.70403147,-0.51374859,-0.76891387,0.38053530,-0.31098360,-0.46540728,-0.82863855,-0.54499954,-0.81566823,0.19391461,-0.11163671,-0.16708884,0.97958314,-0.39579454,-0.39579454,-0.82863855,-0.50212103,-0.50212103,-0.70403147,-0.69365519,-0.69365519,0.19391461,-0.65388960,-0.65388960,0.38053530,-0.27426985,-0.27426985,-0.92168951,-0.70708334,-0.70708334,0.00000000,-0.27426985,-0.27426985,0.92168951,-0.14209418,-0.14209418,0.97958314,-0.14209418,-0.14209418,-0.97958314,-0.69365519,-0.69365519,-0.19391461,-0.39579454,-0.39579454,0.82863855,-0.65388960,-0.65388960,-0.38053530,-0.50212103,-0.50212103,0.70403147,-0.58925140,-0.58925140,-0.55275124,-0.58925140,-0.58925140,0.55275124,-0.69289225,-0.46296579,-0.55275124,-0.76891387,-0.51374859,-0.38053530,-0.69289225,-0.46296579,0.55275124,-0.59044158,-0.39451277,0.70403147,-0.59044158,-0.39451277,-0.70403147,-0.76891387,-0.51374859,0.38053530,-0.46540728,-0.31098360,-0.82863855,-0.81566823,-0.54499954,0.19391461,-0.32248908,-0.21549119,-0.92168951,-0.83144629,-0.55555892,0.00000000,-0.32248908,-0.21549119,0.92168951,-0.16708884,-0.11163671,0.97958314,-0.16708884,-0.11163671,-0.97958314,-0.81566823,-0.54499954,-0.19391461,-0.46540728,-0.31098360,0.82863855,-0.18564409,-0.07690664,-0.97958314,-0.35834834,-0.14841151,-0.92168951,-0.90633869,-0.37540817,-0.19391461,-0.92385632,-0.38267159,0.00000000,-0.51713616,-0.21420942,0.82863855,-0.35834834,-0.14841151,0.92168951,-0.85436565,-0.35386211,-0.38053530,-0.65605640,-0.27173680,0.70403147,-0.76989043,-0.31888792,-0.55275124,-0.76989043,-0.31888792,0.55275124,-0.65605640,-0.27173680,-0.70403147,-0.85436565,-0.35386211,0.38053530,-0.51713616,-0.21420942,-0.82863855,-0.90633869,-0.37540817,0.19391461,-0.18564409,-0.07690664,0.97958314,-0.54899746,-0.10919522,-0.82863855,-0.69649345,-0.13852352,-0.70403147,-0.96215707,-0.19138157,0.19391461,-0.90697956,-0.18039490,0.38053530,-0.38041323,-0.07565539,-0.92168951,-0.98077333,-0.19507432,0.00000000,-0.38041323,-0.07565539,0.92168951,-0.19708854,-0.03918577,0.97958314,-0.19708854,-0.03918577,-0.97958314,-0.96215707,-0.19138157,-0.19391461,-0.54899746,-0.10919522,0.82863855,-0.90697956,-0.18039490,-0.38053530,-0.69649345,-0.13852352,0.70403147,-0.81731617,-0.16257210,-0.55275124,-0.81731617,-0.16257210,0.55275124,0.00000000,0.00000000,1.00000000,-0.20096439,0.00000000,-0.97958314,0.00000000,0.00000000,-1.00000000,0.14209418,-0.14209418,-0.97958314,0.11163671,-0.16708884,-0.97958314,0.07690664,-0.18564409,-0.97958314,0.03918577,-0.19708854,-0.97958314,0.00000000,-0.20096439,-0.97958314,-0.03918577,-0.19708854,-0.97958314,-0.07690664,-0.18564409,-0.97958314,-0.11163671,-0.16708884,-0.97958314,-0.14209418,-0.14209418,-0.97958314,-0.16708884,-0.11163671,-0.97958314,-0.18564409,-0.07690664,-0.97958314,-0.19708854,-0.03918577,-0.97958314};
    unsigned int bufNormLen = sizeof(normElems) / sizeof(normElems[0]);
    compCount = 3;
    elemCount = bufNormLen / compCount;
    csRef bufNormal = csRenderBuffer::CreateRenderBuffer((size_t) elemCount, CS_BUF_STATIC, CS_BUFCOMP_FLOAT, compCount);
    bufNormal->CopyInto(normElems, elemCount); //this is the only available method to copy apparently the data from memory/pointer and into the buffer….
    state->AddRenderBuffer(“normal”, bufNormal);

    //renderbuffers: texture coordinate
    //TO DO TO CHANGE retrieve the contents of the buffer from SMGData
    const float texElems[] = {0.061220,0.172764,0.080647,0.112472,0.135028,0.130630,0.107782,0.187599,0.025234,0.606227,0.028936,0.544141,0.054891,0.552189,0.048386,0.613417,0.051220,0.234138,0.092315,0.247027,0.020989,0.668252,0.040867,0.674443,0.044778,0.295915,0.081872,0.307471,0.015638,0.730152,0.031336,0.735069,0.039986,0.357878,0.073887,0.368405,0.356456,0.039893,0.466638,0.087931,0.448527,0.094534,0.333915,0.062714,0.008043,0.791799,0.017784,0.794887,0.036021,0.419934,0.067156,0.429587,0.138246,0.056841,0.196016,0.081502,0.032439,0.482033,0.060989,0.490881,0.999822,0.727971,1.001015,0.665501,1.020989,0.668252,1.015638,0.730152,0.005301,0.353109,0.006404,0.290637,0.447682,0.023355,0.488610,0.084811,0.998129,0.790429,1.008043,0.791799,0.004397,0.415586,0.034176,0.041338,0.003585,0.478066,0.015221,0.103359,0.002796,0.540546,0.010297,0.165735,0.001965,0.603025,0.007907,0.228175,0.001015,0.665501,0.978668,0.603891,0.976612,0.541519,1.002796,0.540546,1.001965,0.603025,0.964040,0.229802,0.958226,0.167665,1.010297,0.165735,1.007907,0.228175,0.981020,0.666245,0.967719,0.292073,1.006404,0.290637,0.983979,0.728561,0.970429,0.354404,1.005301,0.353109,0.595877,0.028785,0.511577,0.085667,0.988176,0.790799,0.972657,0.416765,1.004397,0.415586,0.904978,0.046051,1.034176,0.041338,0.974661,0.479141,1.003585,0.478066,0.946455,0.105901,1.015221,0.103359,0.945911,0.485214,0.941200,0.423411,0.885287,0.119299,0.823807,0.066742,0.950535,0.547029,0.908873,0.178221,0.955439,0.608804,0.921503,0.238833,0.961079,0.670468,0.929787,0.300102,0.968202,0.731911,0.936013,0.361679,0.647807,0.049313,0.532175,0.090357,0.978319,0.792904,0.952583,0.737970,0.941260,0.678089,0.902582,0.374475,0.893448,0.314080,0.659037,0.072825,0.548166,0.098187,0.968695,0.796706,0.910362,0.435188,0.777305,0.092963,0.917545,0.496040,0.835902,0.140195,0.924693,0.556900,0.864639,0.195793,0.932358,0.617639,0.881675,0.254305,0.825891,0.218332,0.796385,0.165337,0.909472,0.630184,0.899158,0.570827,0.845097,0.274835,0.921611,0.688967,0.859136,0.333015,0.937209,0.746646,0.870446,0.392059,0.657607,0.096698,0.558962,0.108237,0.959449,0.802140,0.880347,0.451545,0.745300,0.120662,0.889690,0.511206,0.851201,0.471783,0.839664,0.413538,0.719784,0.148411,0.650899,0.120164,0.862365,0.530171,0.763610,0.192518,0.873929,0.588406,0.791803,0.243985,0.886774,0.646148,0.811613,0.298972,0.902153,0.702905,0.826879,0.355773,0.922167,0.757809,0.565047,0.119643,0.950742,0.809109,0.882867,0.719668,0.864196,0.665186,0.796418,0.381227,0.780693,0.325402,0.907539,0.771302,0.810089,0.437969,0.641452,0.142821,0.567268,0.131697,0.942759,0.817485,0.822818,0.495127,0.697492,0.175520,0.835477,0.552316,0.735147,0.220347,0.848920,0.609159,0.761226,0.271289,0.823944,0.632559,0.808822,0.576971,0.733105,0.299125,0.709376,0.247895,0.841590,0.686906,0.751695,0.353000,0.863688,0.738990,0.767340,0.408333,0.893414,0.786940,0.781429,0.464419,0.630338,0.164376,0.566457,0.143852,0.935719,0.827110,0.794966,0.520772,0.676891,0.201534,0.929896,0.837788,0.879896,0.804521,0.767315,0.547906,0.753309,0.491989,0.657173,0.226088,0.618082,0.184573,0.782092,0.603438,0.685241,0.274482,0.798714,0.658036,0.706593,0.326609,0.818709,0.710884,0.723996,0.380807,0.844487,0.760584,0.739178,0.436135,0.563300,0.155686,0.825051,0.784147,0.795182,0.736662,0.711466,0.463755,0.697047,0.407985,0.867123,0.823829,0.725311,0.519808,0.604972,0.203174,0.558329,0.166865,0.925640,0.849281,0.739467,0.575708,0.637870,0.248852,0.754885,0.630980,0.662044,0.299554,0.772822,0.684979,0.681041,0.353007,0.745717,0.712709,0.726706,0.658807,0.655961,0.377673,0.639317,0.322626,0.770454,0.763731,0.670392,0.433769,0.805029,0.809363,0.683774,0.490356,0.855308,0.844632,0.697011,0.547009,0.591182,0.219956,0.551952,0.177121,0.923407,0.861297,0.710973,0.603325,0.618699,0.269516,0.923791,0.873467,0.844816,0.866689,0.681363,0.629851,0.668005,0.572713,0.599486,0.287785,0.576832,0.234706,0.696970,0.686034,0.616747,0.343256,0.716668,0.740438,0.631000,0.400016,0.743695,0.791492,0.643669,0.457442,0.783837,0.835890,0.655731,0.515122,0.544481,0.186237,0.760428,0.863335,0.713639,0.819179,0.627046,0.537243,0.616615,0.478315,0.836350,0.889729,0.637946,0.596005,0.562017,0.247230,0.536166,0.194033,0.927547,0.885321,0.650183,0.654292,0.580132,0.303378,0.665037,0.711638,0.594131,0.361033,0.684737,0.767195,0.605914,0.419482,0.571358,0.375584,0.560593,0.316042,0.648805,0.791726,0.630288,0.734412,0.580561,0.435564,0.678348,0.845702,0.589070,0.495737,0.732723,0.891172,0.597536,0.555923,0.831424,0.913428,0.606595,0.615943,0.546824,0.257354,0.527209,0.200363,0.935538,0.896247,0.617071,0.675566,0.573887,0.631595,0.567156,0.570413,0.531336,0.264931,0.517784,0.205114,0.948527,0.905466,0.833915,0.937286,0.581872,0.692529,0.540867,0.325557,0.592315,0.752973,0.548386,0.386583,0.607782,0.812401,0.554891,0.447811,0.635028,0.869370,0.560989,0.509119,0.696016,0.918498,0.580647,0.887528,0.561220,0.827236,0.532439,0.517967,0.528936,0.455859,0.638246,0.943159,0.536021,0.580066,0.856456,0.960107,0.539986,0.642122,0.515638,0.269848,0.508043,0.208201,0.966637,0.912069,0.544778,0.704085,0.520989,0.331748,0.551220,0.765862,0.525234,0.393773,0.507907,0.771825,0.506404,0.709363,0.501965,0.396975,0.501015,0.334499,0.510297,0.834265,0.502796,0.459454,0.515221,0.896641,0.503585,0.521934,0.534176,0.958662,0.504397,0.584414,0.947682,0.976645,0.505301,0.646891,0.499822,0.272029,0.498129,0.209572,0.988610,0.915189,1.095877,0.971215,1.404978,0.953949,0.470429,0.645596,0.472657,0.583235,0.483979,0.271440,0.488176,0.209201,1.011577,0.914333,0.467719,0.707927,0.481020,0.333755,0.464040,0.770198,0.478668,0.396109,0.458226,0.832335,0.476612,0.458481,0.446455,0.894099,0.474661,0.520859,0.404978,0.953949,0.385287,0.880701,0.408873,0.821779,0.445911,0.514786,0.450536,0.452971,0.323807,0.933258,0.441200,0.576589,0.147808,0.950687,0.095877,0.971215,0.436013,0.638321,0.468202,0.268089,0.478319,0.207096,0.032175,0.909643,0.011577,0.914333,0.429787,0.699898,0.461079,0.329532,0.421503,0.761167,0.455439,0.391196,0.381676,0.745694,0.393448,0.685920,0.432358,0.382361,0.441260,0.321911,0.364639,0.804207,0.424693,0.443100,0.335902,0.859805,0.417545,0.503960,0.277305,0.907037,0.410362,0.564812,0.159037,0.927175,0.402582,0.625524,0.452583,0.262030,0.468695,0.203294,0.048166,0.901813,0.157607,0.903302,0.245300,0.879339,0.370446,0.607941,0.380347,0.548455,0.437209,0.253354,0.459449,0.197860,0.058962,0.891763,0.359136,0.666985,0.421611,0.311033,0.345098,0.725165,0.409472,0.369816,0.325892,0.781668,0.399158,0.429173,0.296385,0.834663,0.389690,0.488794,0.263610,0.807482,0.291803,0.756015,0.362365,0.469829,0.373930,0.411594,0.219785,0.851589,0.351201,0.528217,0.150899,0.879836,0.339664,0.586461,0.422167,0.242191,0.450742,0.190891,0.065046,0.880358,0.326879,0.644227,0.402153,0.297095,0.311613,0.701028,0.386774,0.353852,0.382867,0.280332,0.407539,0.228698,0.280693,0.674598,0.296418,0.618773,0.364196,0.334814,0.261226,0.728711,0.348920,0.390841,0.235147,0.779654,0.335478,0.447684,0.197492,0.824480,0.322818,0.504873,0.141452,0.857179,0.310089,0.562031,0.442759,0.182515,0.067268,0.868303,0.294966,0.479228,0.308822,0.423029,0.130338,0.835624,0.176891,0.798466,0.281429,0.535580,0.393414,0.213060,0.435719,0.172890,0.066457,0.856148,0.267340,0.591668,0.363688,0.261010,0.251695,0.647001,0.341590,0.313094,0.233105,0.700875,0.323944,0.367441,0.209376,0.752105,0.206593,0.673391,0.223996,0.619193,0.298714,0.341964,0.318709,0.289116,0.185241,0.725518,0.282092,0.396562,0.157173,0.773912,0.267315,0.452094,0.118082,0.815427,0.253309,0.508011,0.379896,0.195479,0.429896,0.162212,0.063300,0.844315,0.239178,0.563865,0.344487,0.239416,0.058329,0.833136,0.104972,0.796826,0.211466,0.536245,0.225311,0.480192,0.325051,0.215853,0.367123,0.176171,0.197047,0.592015,0.295182,0.263338,0.181041,0.646993,0.272822,0.315021,0.162044,0.700446,0.254885,0.369020,0.137870,0.751148,0.239467,0.424292,0.425640,0.150719,0.118699,0.730484,0.139317,0.677374,0.210973,0.396675,0.226706,0.341193,0.091182,0.780045,0.197011,0.452991,0.355308,0.155368,0.423407,0.138704,0.051952,0.822879,0.183774,0.509644,0.305029,0.190637,0.170392,0.566231,0.270454,0.236269,0.155961,0.622327,0.245717,0.287291,0.131000,0.599985,0.143669,0.542558,0.216668,0.259562,0.243695,0.208508,0.116747,0.656744,0.196970,0.313966,0.099486,0.712215,0.181363,0.370149,0.076832,0.765294,0.168005,0.427287,0.344817,0.133311,0.423791,0.126533,0.044481,0.813763,0.155731,0.484878,0.283838,0.164110,0.036166,0.805968,0.062017,0.752771,0.127046,0.462757,0.137946,0.403995,0.260429,0.136665,0.336350,0.110271,0.116615,0.521685,0.213639,0.180821,0.105914,0.580518,0.184737,0.232805,0.094131,0.638967,0.165037,0.288362,0.080132,0.696622,0.150183,0.345707,0.427547,0.114679,0.060593,0.683958,0.071358,0.624416,0.117071,0.324434,0.130288,0.265587,0.046824,0.742647,0.106595,0.384057,0.331424,0.086572,0.435539,0.103753,0.027209,0.799637,0.097536,0.444077,0.232723,0.108828,0.089070,0.504263,0.178349,0.154298,0.080561,0.564436,0.148805,0.208274,0.495265,0.147144,1.017784,0.794887,0.995265,0.852857,1.032175,0.909643,1.048166,0.901813,1.058962,0.891763,1.065046,0.880358,1.067268,0.868303,1.066457,0.856148,1.063300,0.844315,1.058329,0.833136,1.051952,0.822879,1.044481,0.813763,1.036166,0.805968,1.027209,0.799637};
    unsigned int bufTexLen = sizeof(texElems) / sizeof(texElems[0]);
    compCount = 2;
    elemCount = bufTexLen / compCount;
    csRef bufTex = csRenderBuffer::CreateRenderBuffer((size_t) elemCount, CS_BUF_STATIC, CS_BUFCOMP_FLOAT, compCount);
    bufTex->CopyInto(texElems, elemCount); //this is the only available method to copy apparently the data from memory/pointer and into the buffer….
    state->AddRenderBuffer(“texture coordinate”, bufTex);

    //renderbuffers: color
    //TO DO TO CHANGE retrieve the contents of the buffer from SMGData
    const float colorElems[] = {1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1,1.000000,1.000000,1.000000,1};
    unsigned int bufColorLen = sizeof(colorElems) / sizeof(colorElems[0]);
    compCount = 4;
    elemCount = bufColorLen / compCount;
    csRef bufColor = csRenderBuffer::CreateRenderBuffer((size_t) elemCount, CS_BUF_STATIC, CS_BUFCOMP_FLOAT, compCount);
    bufColor->CopyInto(colorElems, elemCount); //this is the only available method to copy apparently the data from memory/pointer and into the buffer….
    state->AddRenderBuffer(“color”, bufColor);

    //triangles
    const float triangles[] = {1,2,3,0,1,3,5,6,7,4,5,7,0,3,9,8,0,9,4,7,11,10,4,11,8,9,13,12,8,13,10,11,15,14,10,15,12,13,17,16,12,17,19,20,21,18,19,21,14,15,23,22,14,23,16,17,25,24,16,25,18,21,27,26,18,27,24,25,29,28,24,29,26,27,2,1,26,2,28,29,6,5,28,6,31,32,33,30,31,33,35,12,16,34,35,16,37,19,18,36,37,18,30,33,39,38,30,39,34,16,24,40,34,24,36,18,26,41,36,26,40,24,28,42,40,28,41,26,1,43,41,1,42,28,5,44,42,5,43,1,0,45,43,0,44,5,4,46,44,4,45,0,8,47,45,8,46,4,10,48,46,10,47,8,12,35,47,12,50,51,52,49,50,52,54,55,56,53,54,56,49,52,31,57,49,31,53,56,59,58,53,59,57,31,30,60,57,30,58,59,62,61,58,62,64,37,36,63,64,36,60,30,38,65,60,38,61,62,67,66,61,67,63,36,69,68,63,69,66,67,71,70,66,71,68,69,73,72,68,73,70,71,51,50,70,51,72,73,55,54,72,55,75,66,70,74,75,70,77,68,72,76,77,72,74,70,50,78,74,50,76,72,54,79,76,54,78,50,49,80,78,49,79,54,53,81,79,53,80,49,57,82,80,57,81,53,58,83,81,58,82,57,60,84,82,60,83,58,61,85,83,61,87,64,63,86,87,63,84,60,65,88,84,65,85,61,66,75,85,66,86,63,68,77,86,68,90,82,84,89,90,84,92,83,85,91,92,85,94,87,86,93,94,86,89,84,88,95,89,88,91,85,75,96,91,75,93,86,77,97,93,77,96,75,74,98,96,74,97,77,76,99,97,76,98,74,78,100,98,78,99,76,79,101,99,79,100,78,80,102,100,80,101,79,81,103,101,81,102,80,82,90,102,82,103,81,83,92,103,83,105,99,101,104,105,101,107,100,102,106,107,102,104,101,103,108,104,103,106,102,90,109,106,90,108,103,92,110,108,92,109,90,89,111,109,89,110,92,91,112,110,91,114,94,93,113,114,93,111,89,95,115,111,95,112,91,96,116,112,96,113,93,97,117,113,97,116,96,98,118,116,98,117,97,99,105,117,99,118,98,100,107,118,100,120,112,116,119,120,116,122,113,117,121,122,117,119,116,118,123,119,118,121,117,105,124,121,105,123,118,107,125,123,107,124,105,104,126,124,104,125,107,106,127,125,106,126,104,108,128,126,108,127,106,109,129,127,109,128,108,110,130,128,110,129,109,111,131,129,111,130,110,112,120,130,112,132,114,113,122,132,113,131,111,115,133,131,115,135,127,129,134,135,129,137,128,130,136,137,130,134,129,131,138,134,131,136,130,120,139,136,120,141,132,122,140,141,122,138,131,133,142,138,133,139,120,119,143,139,119,140,122,121,144,140,121,143,119,123,145,143,123,144,121,124,146,144,124,145,123,125,147,145,125,146,124,126,148,146,126,147,125,127,135,147,127,148,126,128,137,148,128,150,145,147,149,150,147,152,146,148,151,152,148,149,147,135,153,149,135,151,148,137,154,151,137,153,135,134,155,153,134,154,137,136,156,154,136,155,134,138,157,155,138,156,136,139,158,156,139,160,141,140,159,160,140,157,138,142,161,157,142,158,139,143,162,158,143,159,140,144,163,159,144,162,143,145,150,162,145,163,144,146,152,163,146,165,157,161,164,165,161,167,158,162,166,167,162,169,159,163,168,169,163,166,162,150,170,166,150,168,163,152,171,168,152,170,150,149,172,170,149,171,152,151,173,171,151,172,149,153,174,172,153,173,151,154,175,173,154,174,153,155,176,174,155,175,154,156,177,175,156,176,155,157,165,176,157,177,156,158,167,177,158,178,160,159,169,178,159,180,174,176,179,180,176,182,175,177,181,182,177,179,176,165,183,179,165,181,177,167,184,181,167,186,178,169,185,186,169,183,165,164,187,183,164,184,167,166,188,184,166,185,169,168,189,185,168,188,166,170,190,188,170,189,168,171,191,189,171,190,170,172,192,190,172,191,171,173,193,191,173,192,172,174,180,192,174,193,173,175,182,193,175,195,190,192,194,195,192,197,191,193,196,197,193,194,192,180,198,194,180,196,193,182,199,196,182,198,180,179,200,198,179,199,182,181,201,199,181,200,179,183,202,200,183,201,181,184,203,201,184,205,186,185,204,205,185,202,183,187,206,202,187,203,184,188,207,203,188,204,185,189,208,204,189,207,188,190,195,207,190,208,189,191,197,208,191,210,202,206,209,210,206,212,203,207,211,212,207,214,204,208,213,214,208,211,207,195,215,211,195,213,208,197,216,213,197,215,195,194,217,215,194,216,197,196,218,216,196,217,194,198,219,217,198,218,196,199,220,218,199,219,198,200,221,219,200,220,199,201,222,220,201,221,200,202,210,221,202,222,201,203,212,222,203,223,205,204,214,223,204,225,219,221,224,225,221,227,220,222,226,227,222,224,221,210,228,224,210,226,222,212,229,226,212,231,223,214,230,231,214,228,210,209,232,228,209,229,212,211,233,229,211,230,214,213,234,230,213,233,211,215,235,233,215,234,213,216,236,234,216,235,215,217,237,235,217,236,216,218,238,236,218,237,217,219,225,237,219,238,218,220,227,238,220,240,234,236,239,240,236,242,235,237,241,242,237,239,236,238,243,239,238,241,237,225,244,241,225,243,238,227,245,243,227,244,225,224,246,244,224,245,227,226,247,245,226,246,224,228,248,246,228,247,226,229,249,247,229,251,231,230,250,251,230,248,228,232,252,248,232,249,229,233,253,249,233,250,230,234,240,250,234,253,233,235,242,253,235,255,247,249,254,255,249,257,251,250,256,257,250,259,248,252,258,259,252,254,249,253,260,254,253,256,250,240,261,256,240,260,253,242,262,260,242,261,240,239,263,261,239,262,242,241,264,262,241,263,239,243,265,263,243,264,241,244,266,264,244,265,243,245,267,265,245,266,244,246,268,266,246,267,245,247,255,267,247,268,246,248,259,268,248,270,264,266,269,270,266,272,265,267,271,272,267,269,266,268,273,269,268,271,267,255,274,271,255,273,268,259,275,273,259,274,255,254,276,274,254,278,257,256,277,278,256,275,259,258,279,275,258,276,254,260,280,276,260,277,256,261,281,277,261,280,260,262,282,280,262,281,261,263,283,281,263,282,262,264,270,282,264,283,263,265,272,283,265,285,280,282,284,285,282,287,281,283,286,287,283,284,282,270,288,284,270,286,283,272,289,286,272,288,270,269,290,288,269,289,272,271,291,289,271,290,269,273,292,290,273,291,271,274,293,291,274,292,273,275,294,292,275,293,274,276,295,293,276,297,278,277,296,297,277,294,275,279,298,294,279,295,276,280,285,295,280,296,277,281,287,296,281,300,292,294,299,300,294,302,293,295,301,302,295,304,297,296,303,304,296,299,294,298,305,299,298,301,295,285,306,301,285,303,296,287,307,303,287,306,285,284,308,306,284,307,287,286,309,307,286,308,284,288,310,308,288,309,286,289,311,309,289,310,288,290,312,310,290,311,289,291,313,311,291,312,290,292,314,312,292,313,291,293,302,313,293,316,310,312,315,316,312,318,311,313,317,318,313,315,312,314,319,315,314,317,313,302,320,317,302,319,314,322,321,319,322,320,302,301,323,320,301,325,304,303,324,325,303,321,322,327,326,321,327,323,301,306,328,323,306,324,303,307,329,324,307,328,306,308,330,328,308,329,307,309,331,329,309,330,308,310,316,330,310,331,309,311,318,331,311,333,328,330,332,333,330,335,329,331,334,335,331,332,330,316,336,332,316,334,331,318,337,334,318,336,316,315,338,336,315,337,318,317,339,337,317,338,315,319,340,338,319,339,317,320,341,339,320,340,319,321,342,340,321,341,320,323,343,341,323,345,325,324,344,345,324,342,321,326,346,342,326,343,323,328,333,343,328,344,324,329,335,344,329,348,340,342,347,348,342,350,341,343,349,350,343,352,345,344,351,352,344,347,342,346,353,347,346,349,343,333,354,349,333,351,344,335,355,351,335,354,333,332,356,354,332,355,335,334,357,355,334,356,332,336,358,356,336,357,334,337,359,357,337,358,336,338,360,358,338,359,337,339,361,359,339,360,338,340,348,360,340,361,339,341,350,361,341,363,358,360,362,363,360,365,359,361,364,365,361,362,360,348,366,362,348,364,361,350,367,364,350,366,348,347,368,366,347,367,350,349,369,367,349,371,352,351,370,371,351,368,347,353,372,368,353,369,349,354,373,369,354,370,351,355,374,370,355,373,354,356,375,373,356,374,355,357,376,374,357,375,356,358,363,375,358,376,357,359,365,376,359,378,370,374,377,378,374,380,373,375,379,380,375,377,374,376,381,377,376,379,375,363,382,379,363,381,376,365,383,381,365,382,363,362,384,382,362,383,365,364,385,383,364,384,362,366,386,384,366,385,364,367,387,385,367,386,366,368,388,386,368,387,367,369,389,387,369,390,371,370,378,390,370,388,368,372,391,388,372,389,369,373,380,389,373,393,385,387,392,393,387,395,386,388,394,395,388,392,387,389,396,392,389,398,390,378,397,398,378,394,388,391,399,394,391,396,389,380,400,396,380,397,378,377,401,397,377,400,380,379,402,400,379,401,377,381,403,401,381,402,379,382,404,402,382,403,381,383,405,403,383,404,382,384,406,404,384,405,383,385,393,405,385,406,384,386,395,406,386,408,402,404,407,408,404,410,403,405,409,410,405,407,404,406,411,407,406,409,405,393,412,409,393,411,406,395,413,411,395,412,393,392,414,412,392,413,395,394,415,413,394,414,392,396,416,414,396,418,398,397,417,418,397,415,394,399,419,415,399,416,396,400,420,416,400,417,397,401,421,417,401,420,400,402,408,420,402,421,401,403,410,421,403,423,415,419,422,423,419,425,416,420,424,425,420,427,417,421,426,427,421,424,420,408,428,424,408,426,421,410,429,426,410,428,408,407,430,428,407,429,410,409,431,429,409,430,407,411,432,430,411,431,409,412,433,431,412,432,411,413,434,432,413,433,412,414,435,433,414,434,413,415,423,434,415,435,414,416,425,435,416,436,418,417,427,436,417,438,432,434,437,438,434,440,433,435,439,440,435,437,434,423,441,437,423,439,435,425,442,439,425,444,436,427,443,444,427,441,423,422,445,441,422,442,425,424,446,442,424,443,427,426,447,443,426,446,424,428,448,446,428,447,426,429,449,447,429,448,428,430,450,448,430,449,429,431,451,449,431,450,430,432,438,450,432,451,431,433,440,451,433,453,448,450,452,453,450,455,449,451,454,455,451,452,450,438,456,452,438,454,451,440,457,454,440,456,438,437,458,456,437,457,440,439,459,457,439,458,437,441,460,458,441,459,439,442,461,459,442,463,444,443,462,463,443,460,441,445,464,460,445,461,442,446,465,461,446,462,443,447,466,462,447,465,446,448,453,465,448,466,447,449,455,466,449,468,460,464,467,468,464,470,461,465,469,470,465,472,462,466,471,472,466,469,465,453,473,469,453,471,466,455,474,471,455,473,453,452,475,473,452,474,455,454,476,474,454,475,452,456,477,475,456,476,454,457,478,476,457,477,456,458,479,477,458,478,457,459,480,478,459,479,458,460,468,479,460,480,459,461,470,480,461,481,463,462,472,481,462,483,477,479,482,483,479,485,478,480,484,485,480,482,479,468,486,482,468,484,480,470,487,484,470,489,481,472,488,489,472,486,468,467,490,486,467,487,470,469,491,487,469,488,472,471,492,488,471,491,469,473,493,491,473,492,471,474,494,492,474,493,473,475,495,493,475,494,474,476,496,494,476,495,475,477,483,495,477,496,476,478,485,496,478,19,497,20,39,498,499,38,39,499,37,497,19,64,497,37,65,38,499,88,65,499,87,497,64,94,497,87,95,88,499,114,497,94,115,95,499,133,115,499,132,497,114,141,497,132,142,133,499,160,497,141,161,142,499,164,161,499,178,497,160,186,497,178,187,164,499,205,497,186,206,187,499,209,206,499,223,497,205,231,497,223,232,209,499,251,497,231,252,232,499,258,252,499,257,497,251,278,497,257,279,258,499,298,279,499,297,497,278,305,298,499,304,497,297,325,497,304,500,305,499,501,500,499,345,497,325,502,501,499,352,497,345,371,497,352,503,502,499,504,503,499,390,497,371,398,497,390,505,504,499,418,497,398,506,505,499,507,506,499,436,497,418,444,497,436,508,507,499,463,497,444,509,508,499,510,509,499,481,497,463,489,497,481,511,510,499,29,493,495,6,29,495,2,494,496,3,2,496,6,495,483,7,6,483,3,496,485,9,3,485,7,483,482,11,7,482,9,485,484,13,9,484,489,20,497,11,482,486,15,11,486,13,484,487,17,13,487,488,21,20,489,488,20,15,486,490,23,15,490,17,487,491,25,17,491,21,488,492,27,21,492,498,511,499,25,491,493,29,25,493,27,492,494,2,27,494};
    int i, n;
    n = sizeof(triangles) / sizeof(triangles[0]);
    for (i = 0; i< n; i = i + 3) { state->AddTriangle(csTriangle(triangles[i], triangles[i+1], triangles[i+2][]

January 11, 2020

Eulora’s Client 0.1.2b and Deps

Filed under: Coding,Eulora — Diana Coman @ 5:44 pm

Since it was decided to go back to the old 0.1.2b client code 1 I might as well mirror all of it here and therefore know at all times where to find it. Sure, it was supposedly on Minigame’s website but that meanwhile went down in one of the shitstorms to date and sure, there was supposedly also a bot on Mocky’s site but meanwhile guess what, that one is also not accessible anymore. Good that I have all sorts of backups and local mirrors, as it just “happens”, right? 2

You’ll need cal3d and CrystalSpace (CS) first, as those are the main dependencies of Eulora’s client. Depending on your operating system, you might need to install various other packages (development utils mainly, as well as some font+sound libs and the nvidia toolkit) to be able to compile the whole orchestra. Have a look at the wiki page 3 for step by step instructions on how to install and compile everything.

The archives and my signatures 4 for cal3d, cs, Eulora’s 0.1.2b client and some of the latest versions of Mocky’s extensions of Foxybot:

  1. Yes, it sucks but no, you don’t get to say anything about it at all. Because *unlike you*, I’m really in the best position to *know exactly* just how much it sucks since I’ve been stuck making sense of this code for quite a while now, on top of a huge other pile of stuff that nobody else touches with a ten foot pole. Come first with a clean new client that also works and then -only then- you get to say how much this one sucks.[]
  2. It’s not that hard really, all it takes is a simple wget in the local dir of your choice, for instance: wget –mirror –convert-links –html-extension –wait=2 -o log http://www.eulorum.org/Eulora or, if robots.txt gets in the way of it for your target site, even wget -e robots=off –mirror –convert-links –html-extension –wait=2 -o log http://www.eulorum.org/Eulora[]
  3. Yes, I mirrored locally this one too. Have you? []
  4. ALWAYS check the signatures! If you need my public key, you’ll find it on my Reference Code Shelf. And if you need anything else or are stuck on something, the best option is to simply leave a polite comment and ask for help, you know?[]

December 29, 2019

The Shady Business with Shaders in CS (Notes on Graphics in Eulora, IX)

Filed under: Coding,Eulora — Diana Coman @ 4:32 pm

Over this past week I’ve been going as planned through CS code and docs and everything available to figure out in more concrete detail just how exactly are those fabled “shaders” implemented and used by the graphics engine – this 3rd big tangle of clusterfuck code that fell in my lap for disentangling too, it would seem. And boy did I find everything and anything in there, to the extent that here I find myself, stopping one week in rather than two, since I already need to set it all out 1 in clear so a decision can then be made as to the way forwards.

The initial starting point was the Terrain2 plugin that handles terrain generation using a heightmap and palette of materials. This plugin expects a shader to exist with a specific name even since it relies on it for what it calls “splatting” – basically mixing seamlessly the different materials of the terrain where they change from one to another close enough so that you care about it (at a distance it just uses whatever is defined as “base material”). The trouble with this “expectation” is that it’s one of those blind dependencies that are the mark of CS: the Terrain plugin (being a plugin, ffs!) is supposedly independent of the rest, right? Except, as you can see, it requires this darned shader and that in turn requires all sorts of snippets and other shaders and those require their own loaders and xml and all that jazz that turns out in practice to be thousands and thousands of lines of code. So sobered by this display of true independence and revolutionary zeal 2, I went deeper into CS and looked in turns at how a shader is created in the first place (spoiler: ONLY through a spiderweb of “loading” from XML) and then how it’s used: in bits and pieces, depending on which plugin is chosen and what hardware+software is used. Read on for the gruesome details!

The look at how a shader is created was triggered by this idea that it’s perhaps simply a matter of extracting from the loaders/shader creators whatever they do and package that so it can be done whenever needed. This idea died quickly upon a look at the “loaders” because “what they do” is a lot of obfuscation really: since there are conditions and loops and inclusion of “snippets” from other files and fallbacks and all sorts possible and indeed present in the “xml definition” of a shader, the loading part is an equally complex affair of 7.5k+ LOC where various bits and pieces (e.g. techniques) are collected and then evaluated at a later stage since it can’t even be known upfront which one is useful for what or where or whether at all. I can’t see how it’s any use to aim to disentangle this mess directly really.

For added lulz of the raise-hopes-than-bash-it-on-the-rocks type, I even managed to find in one of the “shader wrappers” a method that was supposedly meant for “loading a shader from source code”, except it contains nothing other than a comment: “Loading from source code is not supported”. And I can easily tell you exactly why it’s “not supported” – because it can’t fuckingly be sanely supported given all that mess, no. No wonder it’s “not supported”.

Next I turned to investigating the way shaders are stored and used throughout the engine so that I can perhaps figure out if it’s possible to either rip them out entirely and replace with a saner approach or at least create my own “shaders” directly on the fly in the code and register those with the engine, effectively bypassing the whole xml-loaders insanity.

In summary, shaders as currently implemented in CS are considered “compiled” parts, as they are indeed obtained solely through a sort of ad-hoc XML-language interpretation. They (and especially the shader variables with all the added complexity of contexts and stack and whatnot) are used throughout the engine: from them being part and parcel of a material definition to the rendering loop explicitly calling the shaders registered for each mesh. As such, ripping the current shader-setup entirely out doesn’t seem very doable without a full overhaul of the engine itself.

There is NO current way to modify an existing shader on the fly and the “shader compiler” class itself (the one producing shaders) pulls in the whole SCF mess with it so that it’s not even all that easy or straightforward to try to make /adapt one directly in code. The *only* currently working way to create a shader and load it into the engine is by using a loader that will trigger god-knows-how-many-levels-deep of xml tokenizing, parsing, evaluating and so on. There are nominally two types of shader loaders, namely for “xmlshader” and “weavershader” but in practice the xmlshader is the basis, as the weavershader simply adds a few more bells and whistles on top, otherwise relying on the xmlshader for the heavy work. The shaders themselves are not really concerned solely with “defining a class of surface” but pack inside additional concerns such as providing alternatives for different types of hardware or software (this is done via providing several ways of doing the same thing), supporting various LOD (levels of detail), reusing other supposedly-existing shaders and snippets, supporting (or not) different rendering plugins (currently OpenGL and software rendering).

Given the above rather troublesome finds, I think it’s worth to go in a bit more detail on the theory of shaders in CS, since it might help to figure out what if any of it is really useful as such (and for what exactly). From a theoretical point of view, shaders in CS sound reasonable enough: each shader is meant to provide simply all the rendering steps and tumbles (or other workarounds) needed to obtain on screen a class of surfaces: e.g. you can write a shader for water-like surfaces, another one for cloth-like surfaces and yet another one for terrain surfaces. There is even a reasonable enough pipeline for this since each shader is meant to contain a clear list of parts 3:

  1. Metadata
  2. Shader Variables
  3. Technique(s)
  4. Pass(es) per technique
  5. Mapping(s) per pass
  6. Vertex processor per pass (if any)
  7. Vertex program per pass (if any)
  8. Fragment program per pass (if any)

The code investigation revealed that metadata is more of an overplanning in there – it consists currently of “maximum number of lights this shader can handle”. And I can’t even quite say why is there a maximum number of lights or why exactly does a shader add lights to start with – since when is a surface to be defined by …added lights? But nevertheless, it is: apparently quite a few “effects” are achieved by adding whatever number of lights in various passes, for all the logic that has.

Shader variables are one of the big things in there really: they are meant to work as parameters for each shader so that conceivably you can reuse a shader to create similar but not identical surfaces. This is also part of where the clusterfuck starts: while each shader can in principle define its own custom variables, in practice there is a set of “predefined” variables (such as “diffuse tex” or “specular tex”) that are assigned different values in different “contexts”. So at rendering time, there is a whole *stack* of shader variables collected from various places and good luck knowing exactly which value will any given variable have where and when. A lick of sanity there is in the form of a hardcoded priority of different contexts so that – according to the docs – the value of any given shader variable will be taken from the highest priority context where it’s found, with contexts provided in order from lowest to highest priority by: shader manager (basically the “global” scope for shader variables), current light, render step, render mesh, mesh wrapper, shader, material. In practice, the result of all this is that everything ends up relying on something else and as a result entirely not separated, despite the whole elaborated pretense to the contrary. Unsurprising by now, I know.

The shader manager for all its pompous name turns out at a closer look to provide only a time variable that is supposedly useful for shaders that aim to animate the surface – why would an animated-surface be needed? I can’t quite tell. The render step and mesh are …”special”. Just in case the rest seemed too sane for your taste, here we have some specialness added since those are not really parameters for shaders – more like some information sources that got stuffed into those “shader variables” since why not. Apparently one can get in here geometry from some meshes (vertices), splatting masks from terrains or even transforms and textures or anything under the sun really. The last three “contexts” (mesh wrapper, shader and material) are the ones most often in use – theoretically the mesh wrapper would allow one to parametrize the surface on a per mesh basis (using the mesh wrapper), per material basis (material context) or otherwise to provide default values in the shader itself. So much support for endless customization that there’s not much room left for *easily* making a darned thing to customize in the first place.

The techniques of a shader are meant to be the actual meat of the shader aka the work done to make that surface look as desired. Customization hits here in the form of several techniques meant as *alternatives*: different ways to do the same thing so that no hardware&software configuration is left behind. To get the full extent of what those good intentions amount to in here, multiply then the number of techniques with the several *passes* (aka multiple renderings for the same darned surface) per technique and further add to it the vertex+fragment processors/programs that come in different flavours depending on the underlying plugin to use (OpenGL or software rendering atm).

The working of a shader in practice as found out from digging through CS sources is meant to be a loop of “passes” where each pass consists in 5 steps:

  1. Activation
  2. Setup
  3. Drawing the mesh
  4. Teardown
  5. Deactivation

If you frown at the 5 steps of which only one does the actual drawing, it’s ok, there’s even more similar sets of substeps in almost each of those 5 steps. In any case, the activation+setup mainly collects and sets in place all the variables and values that are available from the various contexts at that time. This can easily be a whole mess because “variables” can include any number of anything really, from textures and lights to geometry to additional rendering buffers. The drawing of the mesh is handled from what I could tell mainly by the OpenGL plugin (or the software renderer if you run it without OpenGL) and as such the code is dependent on that. The teardown+deactivation do the whole dance in reverse, as expected. Efficiency at its best, wouldn’t you say?

Looking at the classes used for the various components of a shader (shader variables, passes, techniques, programs), the shader variables are the most reasonable in that they don’t bring in at least a whole lot besides what is anyway in there due to materials for instance. The techniques themselves could perhaps be ripped out and repackaged as their internals seem to be mainly direct manipulations of the g3d class (graphics 3d). The main issue though is with respect to the iShader interface itself because it mandates a “QueryObject” that pulls in all of a sudden the whole SCF (basically it forces iShader to be a plugin, just like that), holly shit! And otherwise the shader passes, programs and how to even decide *what* should exactly be done by a shader do if anything at all: which parts are best handled by the material/texture itself and which parts should be done by a “shader” and how does one even make this choice?

As far as I can tell currently, there are a few possible next steps perhaps:

  1. Aim to modify one of the existing “shader compilers” to allow creation from code. This would seem at first sight as requiring the fewest changes to CS itself (though it does inevitably mean that I’m patching CS itself, with all that brings with it, yes) but it comes with some big unknowns, not least of which is: how exactly should a shader be specified in a sane way already (since importing the whole conditions and whatnots is not sane)?
  2. Aim to create directly own version of “iShader” – this means I’ll end up writing now a new “CS plugin”, supposedly less invasive than option 1 above but carrying the SCF burden at the very least. Ripping SCF out of iShader as first step is likely to turn into all sorts of additional trouble and I can’t say that I see the justification for going there even if aiming for this direction. The advantage I can see on this option compared to 1 above – once the SCF is somehow handled, ugh – would be that it’s both more direct (ie should be able then to create and register the shader as/when desired) and perhaps more amenable to a step by step discovery of just what should go in a shader to start with.
  3. Investigate what exactly and how much can be done via texture+materials alone. This is quite iffy because it would probably require quite the dive into Blender now of all things (and the exporter that does not yet exist/work) but I mention it here for completeness.

A big handicap currently here is the lack of clear knowledge as to which parts currently in “shaders” are really needed and to what extent. Basically a lack of practical experience with graphics really, to know how much can be achieved through textures+materials only and how much/which parts require the shader on top to end up as anything reasonable. The current set of shaders is such a spaghetti that I don’t even think it’s worth attempting some direct translation – although I did look through it to start getting some sort of familiarity with how things may be done and to what extent. It all seems so far to be more of a trial-and-error anyway and the code provides further encouragement in this direction e.g. “// If you understand this initially (or at all) I salute you” , “// No idea why we have to invert the Z at all, but reflection is wrong without it” 4

  1. And for my own future reference, so I’m including even “known” parts in here, let them be as I never ever regretted having them written in clear in a single place.[]
  2. Can’t resist humming it too since it’s still better laughing than going nuts: aqui se queda la clara la entranable transparencia de tu querida presencia…[]
  3. Section 4.12.1 in the CS manual gives a reasonable although purely theoretical description for those.[]
  4. Both quotes are from cs/plugins/video/render3d/shader/shaderplugins/glshader_fixed/glshader_fvp.cpp aka the OpenGL plugin that supposedly does the vertex programs parts that may be specified in a shader.[]

December 23, 2019

Foxy’s Unshaded Skin and Side Effects (Notes on Graphics in Eulora, VIII)

Filed under: Coding,Eulora — Diana Coman @ 3:47 pm

Over the past 2 weeks, I’ve been swearing my way through the client’s entrails, as part of the previously planned cutting of some of the abundant tangles that get in the way of everything. It’s been even more gruesome and disgusting than expected – see further for more details on this – but it needed to be done, so it’s done. I have now a client that gets directly to the game instead of going through several screens just to show you anything 1, does not expect to know the whole world upfront, does not imagine it needs the network and a ping-pong server to just show… a local widget and moreover, can even paint something on the bloody screen and react to controls just like that, without a babel tower of widgets and managers and all sorts. And as a tiny satisfaction, it does all this with 3000 LOC less than it took to not do it!

The relatively simple part of this detangling session was the cutting out of network dependency – it took a full day of touching almost every file but at least I had a quite clear idea as to what I could do it: separate the local command handling from the remote “messages” handling (those were grouped together in a single interface, psClientNet without ANY good reason – wtf does local command handling have to do with remote server communication?); remove the inheritance/implementation of network/remote messages handling that was littered everywhere (and I do mean *everywhere*); keep in the active loop so far only what is actually useful.

The less simple task was then to get the client to show something at all because of the tangles between the view, the “psCamera” class and – again, as usual – ~everything else. That was done too and some image made it to the screen but then I realised that … the controls – aka moving anything, even the camera around – were not working anymore! Why? Well, because it turned out that the client’s controls relied on various PAWS widgets being loaded and those widgets relied on one another and on all sorts of graphics being present and known and whatnot. How the fuck can you program like that with the GUI actually *driving* the very definition and use of controls? They are called controls and *not* banana-dispensers already!

Once I found a way to reinstate controls and character manipulation as well 2, the next step was to make *sure* that no shaders are loaded by default somehow & somewhere because indeed, the ever helpful ps&cs would do just that (and otherwise whine loudly if the expected shaders were not found). As a result, I found out another very interesting thing: while CS will load the “default texture” aka “error texture” when the specified texture is not found, this seems to *also* have some unexpected side effects in that all of a sudden, the colours of the terrain itself may simply change from one run to …the next. Here’s the world as it looks on one of those runs with Foxy clad in “error texture”:

noshaders__1_640.png

There is the fact that the terrain plugin expects indeed to use shaders as if they couldn’t possibly ever NOT be there but I still don’t really have the words for this situation where I run the darned thing once and the hills are green, after which I run it again and they are sandy or maybe gray. I’m sure I’ll figure out the why too, just as soon as I get to the bottom of that wonderful terrain plugin but hey, all the fun in its own time, right? For now though, here’s Foxy in her own skin but without any shaders whatsoever, under a starry night sky:
noshaders__2_640.png

The next step now is to figure out just how to do all that shaders are meant to do, directly from CS. An initial mapping of shaders in CS suggests that this is going to be a huge pain really and I’ll have to find a way to cut the knots before going nuts: as the situation stands, CS considers shaders to be “compiled” bits produced by registered “shader compiler” plugins of which there are 2 varieties – a shader “weaver” that depends on the “xml shader”; both of those rely on xml anyway and the whole xml-for-shaders is effectively a sort of “let’s now program in xml” that requires as a result ~7.5k LOC just to… parse xml.

Rather than waste the time disentangling all the above shader mess (it’s not a straightforward thing either, as you might tell by now), my current plan is to try to go at it perhaps from the very practical angle of what and how are “compiled” shaders actually used/useful and how can I produce them as needed. As this is not really all that clear to start with, I can’t say I have a sane way to estimate just how long it will take, but I aim to have at least something more concrete in 2 weeks maximum 3 since one way or another this has to move forwards.

  1. No, seriously, do you *really* want to play? Maybe you just started the client to…quit? Or to see the …credits? Admire the splashscreen? Dunno, something *other* than playing?[]
  2. This involves in usual ps fashion several managers and spurious classes, all tangled up together and calling one another until nobody knows anymore what is where and for what reason, if there ever was a reason; nevertheless, this part – if it is to be cleaned – will have to wait for another day as at the moment there are more pressing matters really.[]
  3. There’s Christmas and New Year in those 2 weeks too but who cares about those all that much.[]

December 6, 2019

Cutting through the Tangles (Notes on Graphics in Eulora, VII)

Filed under: Coding,Eulora — Diana Coman @ 12:08 pm

The goal of this most recent dive of mine into the CS-pit of how to do little in 1001 ways and a lot of lines of cpp&xml 1 was stated in #eulora:

diana_coman:hmm, not sure things are clear there somehow; let me see from the other end: do you suggest I dig in CS and figure out how to directly get from it what I want it to do, ditching all those shaders xmls and whatnots?

As a result of the above, I went first through another re-reading of the CS manual with pen and paper at hand – it’s basically the map but certainly not quite the territory. At any rate, thus fortified hardened of sorts, I turned to the self-doubting CS code 2 itself to figure out some – any! – way of cutting this knot and getting to the core of the engine – aka the *useful* part, ffs! – so that I can finally DO the graphics properly and on the fly, including icons, textures, materials, shaders, effects, terrain, lights, meshes, animations, everything! For the impatient reader, here’s the update coming out of this only yesterday:

diana_coman: mircea_popescu: looking at the “cut straight to doing it in CS” here seems to even come with 2 layers really: first there’s the added cruft on CS-core itself; then there is the layers-upon-layers of cruft added by PS/client.
diana_coman: and there is always that compromise between “supposedly reusing existing code saves time” and the bitter experience of “yes, but reusing THIS code costs time”.

A large amount of cruft in CS comes from the very unfortunate idea of chasing a theoretical notion of flexibility & reusability while ignoring the practical result of the wrong sort of complexity being baked in. To quote from the CS manual itself:

diana_coman: lemme cite from CS manual for the ages: “Crystal Space is a highly modular framework. As such it is more complex then many other game engines or renderers you might encounter. The advantage of this complexity if that it offers you a lot of power in what you do and how you use it.”
diana_coman: or such was the dream there.
diana_coman: to add from only 2 paras down: “the components and libraries are *more or less* independent of each other” (emphasis is mine).

Indeed, CS has as a result of the unfortunate design decision above, a lot of components (“plugins”) and libraries that are quite dependent on one another and in such a tangled way that one is even advised to NOT attempt to load a plugin directly since that will just fail if dependencies are not already loaded! And here it starts, because that requires then a dedicated loader that supposedly knows the dependencies but also works only with values from a config file so nope, not exactly on the fly anyway. Do you see yet how much and what sort of power you gained in return for all this complexity?

If the above is not quite enough, add to it the fact that the pretense at “independent of each other” comes at the very steep price of the horror called SCF (Shared Class Facility) – put simply, this is a way for code to use methods based on known interfaces, supposedly unaware of which/what plugin might happen to implement those methods in any given run. In practice though this simply means that the plugins get bogged down with a wad of code just to “register” their implementations, they are still nevertheless not exactly independent because how can such a thing be in a graphics engine where everything is about the same thing really and finally, the code using plugins has to wrap even the simplest of stuff in nightmarish calls to the SCF so that if you want to change one tiny bit, you still have to write at least 10 lines and remember several levels of layered abstractions that have nothing to do really with the task at hand.

Oh, and lest we forget, all this festival of abstracting things away from your grasp in the name of your own “power of use” further means that trying to find the code that actually gets executed (ie trying to figure out just WHERE in the darned thing shaders for instance are really implemented as such) is an exercise in frustration: for one thing it’s quite often scattered about in several components (there are loaders – hence parsers and tokenizers and whatnots – of xml too in this, for added fun!), for another thing it ends up 25-40 layers deep at most time and for the cherry on the top, since at each point you get only the “abstractions”, tough luck to actually piece it all back together again before you go insane. How, just HOW exactly can anyone hallucinate this to be “power of use”? 3

Still, since I’m not going to rip away the whole SCF madness out of CS 4 – or not just yet, I’m an optimist at heart – it follows that I’m stuck as usual with the task of finding some practical way forward anyway. A closer study of those shaders reveals that they are – like most in graphics… – just one way of doing some things. Specifically, CS shaders are meant to be (ignoring for a bit the added xml nonsense) parametrised pieces of code that describe a type of surface. The description of a surface – hence a “shader” – can get quite complex since one can literally change each vertex of the surface and do so repeatedly, for instance by applying/combining/mixing different textures and techniques over several “passes”. Essentially “shader” is just a name for “programmatically but still laboriously 5 painting pixels of a surface just-so, going around some of the limitations the simpler texture+material surface definition might have”. So on one hand shaders are yet again just an alternative to a simpler option but on the other hand they are supposedly the more powerful alternative – the question being of course: is this the same sort of power as discussed just above, namely the power to push the boulder uphill rather than the power of building anything?

Leaving aside for a bit the shader-power-doubts, the dig in CS revealed that the terrain system anyway quite expects to make use of shaders going also through all the dances of shader variables and contexts 6. So at this point it seems that there aren’t all that many options really and at least *some* sort of shaders will have to be supported and therefore sanely extracted/plugged directly into CS and then – possibly – the terrain system either beaten into shape too or at the very least made to work with those. Such joy.

Before ripping into CS code directly though, there’s the added bonus of the client code itself. The original PS code added its own wrong sort of complexity on top of that pushed through by using CS. In a similar ill-fated attempt at “power if only in theory”, there’s a whole system of widgets that are meant to be independent but cannot be so and moreover there are a lot of “managers” that supposedly are independent too but rely on all sorts and in general just about ~everything has two significant dependencies: the “netmanager” aka direct communication with the server 7 and the fact that everything has been already loaded because the whole world is in fact fully known before you even get to push the Start button.

Given that both the above dependencies of PS code are torn to shreds for Eulora’s client, it quite follows that the minimal cut here is along the lines of ditching those parts entirely – while this WILL mean changing a lot of code, I still think it makes for a smaller cut than attempting to work around it. Basically it’s again that frontal assault vs outflanking option: the whole mess is so wide and spread that it’s not worth going around, even if there will be quite a bloody job to cut through it.

Considering all the above, there is some work to be done further on both CS and client code. However, since the client is anyway the final goal and the most effective “tester” of anything of interest, I’ll first take some steps on cutting out the useless but very burdensome tangles in there: going from the starting point (and keeping to a minimum of changes still but don’t count on this to mean “code will not be touched), the client should simply focus on what is does, namely user interface and nothing more. This means that it will display to the user whatever information is *available and relevant*, it will accept input from the user (and make it available for further processing if it involves anything other than UI/display changes) and it will further expose ways for *everything* to be reloaded/changed on the fly, since this is the hard requirement anyway.

Once the client gets to the stage where I can use and test more directly (aka loading on the fly instead of going through 1001 useless other steps just to get to the one I want) a shader with it, I’ll switch again to CS and cut through so that a shader can also be specified and loaded on the fly without any xml involved. As this all adds up to a huge ball of tangled unknowns, there’s very little sanity to any overall estimate at this stage but there will be for sure updates on the progress, as usual. At any rate, the cuts will be strictly on an as-needed basis and done step by step so that there will be gradual improvement while maintaining at all times a running client overall.

It’s worth noting also that the above approach of cutting through the tangle in the client too is the only one that I can currently see to move forwards. Keeping otherwise around all the dead weight just about got to the point where it would stall almost everything trying to push further so it needs to go and good riddance.

  1. To be fair, PS is the champ here and by far! While CS may be doing little with many lines of cpp&xml, PS does way less and with 10 times more lines of the same cpp&xml.[]
  2. Literally, as I illustrated in #eulora: diana_coman: in CS questions-from-the-code: “ctxt->iview = other.ctxt->iview; // @@@ Is this right?”[]
  3. I get it, one can on occasion feel very powerful – Popeye muscle-style! – just for getting this to work at all. As in, it took a LOT of pushing this uphill for it to go smoothly down the other side! Is this the sort of power you are after?[]
  4. Mainly because that is ~same with re-writing the engine really, could just as well start from there if it gets to it.[]
  5. Computer graphics is this precise achievement of programmatic-but-still-laborious, it’s a wonder.[]
  6. The same variables can be and indeed are overwritten in CS at various levels so that you can go properly insane; for instance, you might have set the texture as opaque in the material but then you failed to set it in whatever shader got pulled in somewhere and so fuck you, the whole thing will be totally shiny instead. Did I mention how much I like computer graphics?[]
  7. You may wonder why would every widget and every class need to directly communicate with the server anyway, sure; there are plenty of answers to such wonder and none of them any good.[]

November 5, 2019

Eulora (S.MG) – Taking a Different Sort of Stock

Filed under: Eulora — Diana Coman @ 7:25 pm

The bill of work was set last Thursday in my own words:

…look at them in the light of the new situation: are those sane business choices that help S.MG thrive or are they dangerous ideal choices that will sink S.MG in the end, all the more painfully for the prolongued agony that existing resources can allow? I tell you that I don’t quite see what *else* can I do but at the same time, I also don’t quite see how can this can be done with any guarantees.

Given the above task and the very issue at hand, I chose to start the work from what I see as hard, *usefulness* requirements for S.MG’s tech overall and otherwise server and client sides in particular 1:

Server Side

  1. Reliable – the server code 2 should do exactly what I expect it to do, not more nor less. Easy to state, harder to guarantee given the whole environment otherwise but this requirement won’t go away just because it’s not a walk in the park.
  2. Secure – while this is arguably included in “reliable” above, it’s worth mentioning it on its own too because Eulora’s currency, the ECu, is pegged to Bitcoin and transactions as well as fortunes of players in the game can be quite hefty. Some items can be worth a fortune just by themselves, at least as far as the players are willing to bid to get them.
  3. Stable – Eulora is an always-on game really, there shouldn’t be “down” time, no matter how much and what players throw at the server essentially. Some regular and minimal maintenance time 3 may be all right but the plan is to keep such things to a very minimum (and predictable!) amount of time.
  4. Deployable in commercial settings – from a purely technical point of view I think I could state this along the lines that the server should be architecture-independent perhaps but this is already a reduction of the actual requirement. Eulora’s server code is not meant to be public and as a result, there is the technical as well as non-technical aspect to deployability: it should be deployable in a variety of environments (hardware and software) but it should also keep its secrets, as intended.

Client Side

  1. Reproducible build – given that currently there is only one client and moreover that this one client is going to be whatever I release, I’d much rather have a fixed and reproducible build that won’t be fucked up by shitgnomes armed with all sorts of updates. This might be of course not *fully* possible as stated in practice and I don’t intend to chase it *more than it’s worth* but the direction is there and the more fixed (ie won’t need to fiddle with it if you have a different computer than mine) the building process is the better.
  2. Secure and flexible communications with server – see the ECu and Bitcoin at server side above. As the game proceeds effectively through an ongoing communication between client and server, there has to be a way for players to decide on the exact compromise they are happy to live with in terms of security of their communications and associated cost (including costs of all sorts, for instance convenience as well as bandwidth).
  3. Full decoupling of client from server & game version – in particular, there should be basically no pushing of “updates” on the user/client; the client should simply be able to discover and request + download new game content of any sort, as it becomes available on the server (ie the client pulls the updates if/when it wants to).

There could be more added above to both client and server but I think those are the most relevant currently and for the task at hand.

Armed with the above, I started looking at everything Eulora related, on three main directions: hardware dependencies, software dependencies, design decisions.

  1. Hardware dependencies
  2. TRNG (Fuckgoats aka FG)

    The main hardware dependency currently is the FG(s) provided until now by S.NSA and used as source of entropy for all encryption matters on both client and server. I reviewed the item and its design documents that are quite clear and as far as I can tell currently complete. Ideally I would take the time to procure the needed parts and make a few FG myself to be entirely satisfied that S.MG can indeed proceed on the assumption that there is a working TRNG connected at all times. However, the ideal is one thing and practice quite another: so far I haven’t actually built an FG and therefore the most I can say at this time is that this latest review of existing items and documents did not reveal any potential trouble that I can see. Moreover, the S.MG code does not make a lot of assumptions about the source of entropy being specifically an FG – this means that changing the source of entropy at a later time should not be problematic for S.MG: the reading of data is isolated and can be replaced without a major overhaul of everything. Based on this, I wouldn’t recommend any specific change to the code or approach regarding TRNG at this time.

    Other than the FG, the initial perceived dependency on AMD architecture does not hold: both client and server have been successfully built and deployed on both AMD and Intel computers. While I haven’t tried – nor do I have the hardware for such attempts – to further check it on other architectures, I don’t think there really is a need at this time for exploring more exotic architectures.

  3. Software dependencies
  4. A significant fixed dependency is on gcc 4.9 (or earlier but so far tested on 4.x versions only) and python 2.x (tested on 2.6 and 2.7 but in principle < 3).

    Both client and server have been successfully built and deployed on the following distributions: CentOS 6 and 7, Ubuntu 14.04, Gentoo (several versions but I’m at a loss as to how to fully pinpoint one down since I’d need to list ALL versions of all packages by the looks of it, ugh). The server has also been compiled and ran on Dulap-Gentoo (aka as proto-cuntoo). It’s worth noting that OS versions especially for server side are a potentially troublesome spot if S.MG has to rely on commercially available versions because the preferred versions are increasingly hard to find in the wild (CentOS 6 for instance seems to be set for vanishing from March next year) and new systems bring in a *lot* of changes that are a huge ball of unknown at best (and a complete disaster at worst). Dulap-Gentoo is a frozen snapshot and as such reproducible but essentially a dead-end since there’s nothing supporting it.

    It’s unclear to me what concrete options does S.MG actually even have regarding OS on server side especially, since there seem to be:

    • Dulap-Gentoo: frozen snapshot, reproducible but dead-end.
    • Cuntoo: frozen snapshot, reproducible but currently as dead-end as Dulap-Gentoo since nobody is/has been working on it for quite a while (and the amount of work required is considered high anyway); it also requires further significant effort to fully move all dependencies to static building and to Cuntoo itself
    • CentOS 6: dead-end as unsupported by anyone and otherwise set to most likely vanish commercially from March 2020.
    • Other distro: at best unknown, most likely incompatible with required versions of gcc & python at the very least.

    Other than OS, the most significant software dependencies are CrystalSpace, Cal3D and MySQL, together with the autoconf/make/jam/ftjam building systems that CS especially depends on 4. Those force in turn quite a few other dependencies (CrystalSpace is the champion at how many dependencies it pulls in) but it’s worth noting that most of those are on client side (ie ultimately something that is more flexible anyway and up to players rather than S.MG directly). The more troublesome aspect here is the potential clash between versions of everything required, especially in the context of different OS versions (and corresponding python+gcc) than the ones already checked above. One promising direction as far as I would say now is mainly due to using GNAT as it comes bundled with gcc and therefore one could perhaps ride on that even on a different system especially if everything is indeed moved to static linking (see below at design decisions). This is however not fully investigated (ie to which extent it’s enough & how it plays along with everything else).

  5. Design decisions
  6. A significant decision was the move towards Ada and GNAT as language and building environment, essentially away from C/CPP and all the auto/make tools. This move is still in progress in that some components (CrystalSpace most notably) still require autoconf and jam/ftjam. I looked again at all of this but I can’t see any specific issue or problem in the long term with Ada and GNAT. There is of course the sjlj aspect for which the previous investigation still holds as far as I see. There is also the aspect of GNAT not working on ARM architecture but as far as I can see this is not a significant problem as there is no reason why S.MG should support or be concerned with ARM. If anything, I’d say quite on the contrary, removing concerns about the ARM architecture and its lack of GNAT makes more sense to S.MG (I don’t see why should Eulora care specifically about ARM). The ARM architecture was pushed to some extent because of the Rockchips but that is no direct concern for S.MG from my point of view.

    Overall, the move towards Ada and GNAT still seems to me both sane and essentially benefic to S.MG as compilation with GNAT 5 is so far way more predictable in all the environments I tried (see above): even when autoconf & co failed because of various version clashes or packages not found, the main advantage with GNAT is that it comes as a standalone package and as such, so far, quite portable. If anything, I’d say that the main rub here is at most in the sense that this has to be moved further even more & faster: the only chance that I currently see for any sort of reliable building process is with GNAT & full set of dependencies at the very least, ideally statically linked as well; we are not there yet (though some steps are done towards that, in particular for Cal3D, server components and client-core) and moving CS to this is going to take quite some time and effort.

    From a structural point of view, there wasn’t any direct input from S.NSA given that the originally planned consultation failed to materialise on time. As such, I didn’t spend a lot of time re-evaluating the structure of the code base but at any rate, I don’t see any specific problems with the current direction there. There is indeed a worryingly big amount of effort still required to get to the point of releasing the new client and thus moving on to next stage but this is unrelated to S.NSA as far as I can see. In terms of the code itself, the parts reviewed more closely are “sane MPI“, the UDP lib, Serpent and FFA. As before, it’s still the MPI part that I’m most uncomfortable with and that mainly because it’s still a recovered artefact of dubious origin and with previously found holes and cockroaches. The UDP lib, Serpent and FFA did not reveal anything I didn’t know about and I don’t see any problem with them as they are currently on my Reference Shelf.

With respect to FFA Ch1-Ch19, I want to say that I’d rather put it already to use (even in parts if and where that fits) instead of waiting (as I’ve been waiting even while doing EuCrypt out of necessity) for the next steps and the eventual fruition of a FFA iron or similar. Essentially at this stage the part glowing red for me here is that I sunk some time and effort into FFA and so far S.MG did not directly benefit from it at all so the sane approach would perhaps be to move towards extracting already some benefit from it too. However, there is of course some cost attached now to taking this step of integrating & using bits and parts of FFA.

Mainly for the sake of not leaving stuff out, I feel compelled to add to this list the decision to use V for code released by S.MG so far (e.g. Eucrypt). Here as with FFA, my only concern is more to do with the stalling that seems to have happened for quite a while but that stalling is arguably part of a broader issue. At any rate, if there is something dangerous that I can see here is perhaps just the cumulative effect of all those useful parts that are stalled at one point or another: by the looks of it, S.MG ends up having to push or otherwise support the development of all of them and this is a matter I’d rather discuss in more detail at this stage.

  1. There is of course the other starting point as well, namely the tech, going at it from code up as it were. It’s possible and one can even go at it with an initial list of items that used input from Stanislav/S.NSA – though characteristically, it’s easier to figure those out from my own Reference Shelf and notes than from anywhere else, his blog included. Nevertheless and especially in this case, I needed to… arm myself, there’s no better word for it. Hence first listing the main practical requirements and only then looking at everything and aiming to discern value or lack of it with those hard requirements always in mind.[]
  2. Like ALL the code and all the machines, ffs already![]
  3. There used to be such regular maintenance on Wednesday evenings, usually less than 10 minutes of down time.[]
  4. Especially on client side there are a LOT of dependencies but 1. those have nothing to do with S.NSA 2. they are ultimately players’ concern although currently they have to count for S.MG too given the desired client release.[]
  5. Both Adacore’s version and Ave1’s.[]

September 27, 2019

Pretty Petty Particles (Notes on Graphics in Eulora, VI)

Filed under: Eulora — Diana Coman @ 1:11 pm

As the latest client data hierarchy draft revealed a gaping hole regarding the exact use and requirements of “effects”, I’ve spent those past days diving into the tediousness of “particle systems” – those are the core of CS effects since they are used to simulate rain, snow, explosions, fires, fountains etc. On the bright side, particle systems are actually explained in a bit more detail than other things in the CS manual, mainly because they have been apparently changed quite significantly so the manual’s author felt the need to provide examples for using the new code. On the darker side, they are stuffed into the clunky system of various plugins + xml + factories + meshes + shaders + 1001 attributes to set for the smallest thing, so that 3 drops of rain get you sweating and swearing through the usual multiple layers of all-possible-things in slightly-different-ways.

To start with, trying to find a proper place where those particle systems belong in a sane hierarchy of CS items is rather dubious because particle systems are a combination of almost everything in there plus “emitters” and “effectors”. And ~everything has to do the dance of factory+instance although each time pretty much everything gets re-set anyway 1 so it all feels like an exercise in bureaucratic-coding, to make one’s stomach heave anew. Anyway, the core components of particle systems are those:

  1. A non-moving mesh that is the “particle”. Like all other non-moving meshes, this can naturally have one or several materials/textures and shaders (with the load of shader variables) but unlike all other non-moving meshes, the geometry is not something one sets explicitly via vertices and triangles and all that – instead, the geometry is fixed and set by the provided “plugin” 2 for particle systems! Setting various parameters allows one to tweak this geometry to some extent but that’s about it. According to the manual, the existing plugin simply makes all particles rectangles – you can set however their size and their orientation with respect to the camera.
  2. Any number of emitters. As the name suggests, the emitters create and launch about new particles according to another set of parameters. For the same particle system, one can in principle add any number of emitters but there has to be at least one emitter for anything to happen, of course. There are a few types of emitters that differ mainly in the shape that is covered by the launched particles: sphere, box, cylinder, cone.
  3. Any number of effectors. From my tests, those are not even mandatory. They provide some ways to influence the existing particles (usually their movement but supposedly any aspect of them; the manual says that effectors “put different kinds of effects” on particles, “be it physical or non-physical effects”). There are only three available types of effectors: force, lincolor (interpolates the colour of each particle between two values based on the remaining time to live) and forcefield.

Once a particle system is created, the way it works is relatively straightforward: the engine calls on every frame the update loop of each particle system that is visible to the user; the update loop deletes old particles, polls all emitters to emit new particles, polls all effectors to apply their effects and then performs the final transformations if/when such are required. Basically a particle system is designed as a sort of predefined show happening at some place in the world for some specified interval of time with rectangle-shaped actors only (but possibly various costumes via materials).

For practical tests, I’ve created from code several types of particle systems including “rain”, “fountain” and “fire”. While the general mechanics of it all seem to work fine and the parameters seem to do indeed what is expected, there is still some weird issue with the materials – all the stuff looks brown-ish, no matter what texture or material is loaded. Since otherwise a material is just a material and it’s clearly loading fine for anything else, I quite suspect that the trouble here is the shader: although in principle any shader could be used, there is a specific particle_shader that is used but not really documented anywhere (ie there is the xml of course but that has some variables and possibly references and it is *one* area that I rather avoided to fully get into, so far). Added on top of all previous experience, I’d rather say that sooner or later I will still have to dig shaders too, what can I do. Anyways, at the moment I am mostly interested in having the mechanics in place and figuring out the parameters, so in this sense it’s a success, here’s some snow and rain falling down on naked girl, at times with erupting particles too:

Euloran Snow - it's more brown than white!

Euloran Snow - it's more brown than white!

Undisturbed euloran woman among erupting stuff and pouring rain.

Undisturbed euloran woman among erupting stuff and pouring rain.

Let it rain with brownish nothings.

Let it rain with brownish nothings.

Given the above, I’ll mark the “particle systems (Air)” as explored for now and move on to procedural textures (Water) to see what further catralliard of parameters I find there too. The test of movement for effects (ie following a char or something) is still on the list too but I see it as more general really – since the whole particle system is simply a mesh, having it follow another mesh should be just a matter of giving its position as relative to that rather than absolute. At the moment I’m not sure where exactly would be the best place to do this but I think it requires a bit more consideration because it’s possibly a more generally useful thing to have – some items/characters might simply follow others at some point/for some time, anyway.

  1. It would even be hard to keep track of what is same and what is different given how many darned setThis and setThat one calls.[]
  2. The plugin’s code can be found in CS/plugins/mesh/particles/object/.[]

September 14, 2019

Eulora Client Data Hierarchy v2.0

Filed under: Eulora — Diana Coman @ 9:38 pm

This replaces the previous draft and is based on the new knowledge of client entrails of all sorts as acquired during those past 2 months. It is however still a draft in that it uncovers some new questions (at least new to me) and it requires anyway some feedback and discussion given its scope and crucial position in the SMG Communication Protocol. It’s published as it is at this stage and by the promised deadline not because it’s “complete and final” but mainly because I need to have this version exposed for further discussion in order to be able to advance it further.

While I was already trying in the previous draft to figure out more clearly the scope and nature of the desired hierarchy, this became clearer only after getting a better understanding of the various data the client uses and especially after further discussion in the SMG boardroom of the protocol specification. The previous observation that there are 3 types of information in there (structure, description, representation) is as correct now as it was then. But meanwhile, as I got to understand better all the different domains involved, the high-level view evolved to this:

  1. Game World
    1. Concrete objects
      1. Terrain
      2. Water
      3. Air
      4. Characters
      5. Structures/Items 1.
    2. Abstract objects
      1. Time
      2. Self
      3. Sector
  2. Representation
    1. Graphical
      1. 3D
      2. 2D
      3. Skin/GUI
    2. Audio

Before going further and into the hairier details, it’s worth stating the important distinction that the protocol specification makes between the two types of “resources” that the client can ask for:

  1. Files – relevant protocol messages 4.4 File Request and 4.3 File Transfer.
  2. Objects – relevant protocol messages 4.7 Object Request and 4.8 Object Info (ie “Transfer”)

The Files are – from the protocol’s point of view – “big” resources that never contain other resources. Files are referenced by their full name that works therefore exactly like an id (although a text-id rather than a numerical id).

The “objects” are – from the protocol’s point of view – “small” resources of a potentially composite sort. This means specifically two things: any object will always fit into one single message; an object may contain references to any number of other resources (ie references to other objects and/or to any number of files). An object is given (via a 4.8 message) as a list of tuples (id:type). Each such tuple is the reference (the “id” part) to one “property” of the object, where the term property is quite generic: any object can be at some time or another a “property” of another object. Note also that “id” is not necessarily a numeric id – a filename (with full path included, where relevant) is effectively the “id” of the actual file.

The hierarchy starts with the single object that has no id (otherwise put it has the “null” value as id). Since all things have to start from somewhere, so does Eulora’s language of knowledge – in the beginning, it is always “null”. If you think of the hierarchy correctly as a tree, this object with “null” as id is the root from which there can be ultimately any number of levels – the hierarchy is *not* restricted on either direction (ie any node can have in principle any number of children and any path may be arbitrarely long). The leaf nodes will *always* have “Final” as given type in the tuple (id:type). The id in the tuple (id:type) of a leaf node is either a filename (ie the id of a file, one can think of this as value given by reference) or otherwise a value that is used directly as it is, as data (e.g. helmet.png:Final or 5:Final). All values given at a leaf node will be one of the following basic types:

The root of all the world (the object with “null” as id) is the object that the server sends (via a 4.6 World Bulletin message) in response to an Object Request (4.7) that contains an unknown/non-existent object id. The hierarchy as I managed to flesh it out so far is given as id:type with comments after > :

  1. Root > there is only ONE object of this type and that object has id “null”

    1. Top_Location_ID : Sector > current sector; player’s character is somewhere in the subtree of this object (eg may be in a room that is in a house in this sector, perhaps).
    2. Self_Info_ID : Self > id of the abstract object, not the id of the player’s concrete character object.
    3. Current_Date_ID : Date > euloran date
    4. Current_Time_ID : Time > euloran time
    5. Client_Skin_ID : ModSkin > graphical user interface resources
  2. Sector

    1. CurrentNameID : Name

      • Value (text): Final
    2. Terrain_ID : Terrain > the heightmap (+ material palette)
    3. Air_ID : Air > ambiental light and weather (?)
    4. (Light_ID : Light)* > NB: zero or more; those are purely sources of light (NOT ambiental light which is a property of Air), without physical representation other than the light itself.
    5. (Water_ID : Water)* > bodies of water
    6. (Character_ID : Character)* > anything that is active/can move;
    7. (Structure_ID : Structure/Item)* > anything that is passive/can’t move and is not terrain/air/water/weather
  3. Self > this includes all the stuff that is specific to self (ie known only about own Character) as opposed to the generic info given via Character type (ie info available for all characters encountered, whether self or not)

    1. CurrentPlayer_ID : Character > player’s actual character with data that everyone can access/see
    2. Current_Loc_ID : LocID > should the location ID be here? (otherwise in principle client can fully search the tree until it finds CurrentPlayerID as contained in some location)

      • Value (uint32): Final
    3. Date_Of_Birth_ID : DOB

      • Value (date) : Final
    4. Vitals_Profile_ID : VitalsProfile
    5. Skills_Profile_ID : SkillsProfile
    6. Inventory_ID : Inventory
    7. Equipment_ID : Equipment
  4. Inventory

    1. (Item_ID : Structure/Item)*
  5. Equipment > How is equipment different really from Inventory? I can’t see any real reason to make it a separate type. “Equip” is an action and as such it will fail if the item doesn’t “fit” the destination, be it because it’s not equippable or whatever. Similarly, the “how it looks” is a matter of checking what is in some slots if one so wants. The only thing: the meaning of “positions” in inventory should be defined, at least for those that have a specific meaning beyond “in the bag”.

    1. (ItemID : Structure/Item)*
  6. Date > this could be in principle simply unixtime number but that wouldn’t match well/easily with custom year/month sizes (?) + presumably custom month names rather than numbers (?)

    1. Year_ID : Year
      • Value (text) : Final > should those be numbers?
    2. Month_ID : Month
      • Value (text) : Final > should those be numbers?
    3. Day_Of_Week_ID : DoW
      • Value (text) : Final > should those be numbers?
  7. Time

    1. Hour_ID : Hour
      • Value (uint8) : Final
    2. Minute_ID : Minute
      • Value (uint8) : Final
    3. Second_ID : Second
      • Value (uint8) : Final
  8. ModSkin > should this cover *also* layouts & windows & UI components or only strictly “art” resources? If it’s the later, in principle it’s simply a list of images (with at most width, height, alpha, transparency)??

    1. (UI_Art_ID : UI_Image | UI_Spritesheet) *
  9. UI_Image

    1. (Name_ID : Name)? > CS uses everywhere names for IDs and sprite sheets won’t be fine with filename as name for each sprite; onth perhaps client should just generate its own “names” out of object id and/or filename if it needs them?

      • Value (text) : Final
    2. Size_2D_ID : Size

      • Value (size2d) : Final
    3. Transp_ID : Transparency

      • Value (rgb) : Final
    4. Alpha_ID : Alpha_Lvl

      • Value (float) : Final
    5. File_ID : Image > should all be .dds or .png or fixed format?

      • Ref (text) : Final
  10. UI_Spritesheet

    1. UI_Image_ID : UI_Image > for the sheet as a whole, it still has all the details
    2. (UI_Sprite)+
  11. UI_Sprite

    1. (Pos_ID : Position)? > for sprite sheets – position in parent image

      • Value (position) : Final
    2. (Name_ID : Name)? > CS uses everywhere names for IDs and sprite sheets won’t be fine with filename as name for each sprite; onth perhaps client should just generate its own “names” out of object id and/or filename/whatever if it needs them?

      • Value (text) : Final
  12. VitalsProfile

    1. (VitalID : Vital)*
  13. Vital

    1. Name_ID : Name
      • Value (text) : Final
    2. Current_Value_ID : CurrentValue
      • Value (uint8) : Final > assuming that those are still given as % of max
  14. Skills_Profile

    1. (Skill_Group_ID : SkillGroup)*
  15. Skill_Group

    1. NameID : Name
      • Value (text): Final
    2. (Skill_ID : Skill)*
  16. Skill

    1. Name_ID : Name
      • Value (text): Final
    2. Rank_ID : Rank
      • Value (uint64): Final
    3. Practice_ID : Practice
      • Value (uint8) : Final

      > assuming given as %

    4. Knowledge_ID : Knowledge
      • Value (uint8) : Final

      > assuming given as %

  17. Character

    1. Name_ID : Name
      • Value (text): Final
    2. Sex_ID : Sex > is this given and in what form? is it known for anyone or just for self?
      • Value (uint8): Final
    3. Position_ID : Pos
      • Value (position): Final
    4. Rotation_ID : Rot
      • Value (rotation): Final
    5. Model_3D_ID : ModCal3D
    6. (Model_2D_ID : Mod2D)? > 0 or 1
    7. (Model_Audio_ID : ModAudio)? > 0 or 1
    8. (Model_Effects_ID : ModEffects)? > 0 or 1
  18. Structure/Item > the difference between structures and items is minimal to the point of inexistent (eg “structures” can’t be picked up perhaps, but that’s not a property – simply a response to an attempted action and as such of no interest/relevance here!; or structures will have stack count = 1 but that doesn’t mean that they don’t have stack count…)

    1. Name_ID : Name
      • Value (text): Final
    2. Stack_Count_ID : StackCount > this will just be 1 for houses / some cases.
      • Value (uint64): Final
    3. Quality_ID : Quality > this may be called “durability” for eg tools or houses but it’s same thing anyway.
      • Value (uint64): Final
    4. Position_ID : Pos
      • Value (position): Final
    5. (Model_3D_ID : ModCal3D)? > ONE of the models 3d/2d should be present in general
    6. (Model_2D_ID : Mod2D)? > 0 or 1
    7. (Model_Audio_ID : ModAudio)? > 0 or 1
    8. (Model_Effects_ID : ModEffects)? > 0 or 1
  19. Terrain

    1. NameID : Name
      • Value (text): Final
    2. Position_ID : Pos
      • Value (position): Final
    3. Height_Map_ID : Heightmap > NB: while both materialmap and heightmap are images (and even of same type + same size, listing them as “IMAGE” type means that the role (i.e. which one is Heightmap and which one is MaterialMap should be defined somewhere else – would that be better/how?)
      • Ref (text): Final

      > filename, hence “Ref”, currently “Island.png”

    4. Material_Map_ID : Materialmap
      • Ref (text): Final

      > filename, hence “Ref”, currently “Island_alpha.png”

    5. (Material_ID : Material)* > material palette to use (eg sand, grass, stone)
  20. Air

    1. AmbientLightID : AmbientLight > all lights should be “dynamic” or at any rate the static/dynamic distinction is entirely client-side concern ; currently those values are given at different “times of day” + interpolated by client in between, but I don’t see the *need* for hours in here; client can decide on its own if /when it wants to sample this and what/whether to interpolate or do anything else with the values it gets.
      • Value (rgb) : Final
    2. Weather_ID : Weather
  21. Water > is this needed as separate type? CS has 4 types of procedural textures: fire, water, dots, plasma
    > therefore “water” could simply be in principle an item/structure with the “water” texture applied – TO CHECK.

    1. Item_ID : Structure/Item
  22. Weather > CS uses “particle systems” for this sort of thing (i.e. actual snow/rain)
    > as such, those could be just part of the “sector” at some point + corresponding changes to light & sky, if any. TO CHECK
    > however, there can be perhaps the point of having this separate to set specific params such as emission rate, velocity etc. ?

    1. Particle_Syst_ID: Particle_System > if going this route, this is TBF
  23. ModCal3D > aka moving/character stuff

    1. BB_Size_ID : BoundingBox > this is used for collision detection

      • Value (size3d) : Final
    2. Skeleton_ID : Skeleton > this is MANDATORY; all moving stuff have to have a skeleton

      • Ref (text) : Final

      > this is a .csf file for now; should the format be a further level/property?

    3. (Animation_ID : ModAnim)* > a character may have any number of animations
    4. (Submesh_ID : ModMesh)* > parts/subparts of the model; NB: atm sockets is still not used/unclear!
    5. Scale_ID : Scale > intended scale of the model, this is a transformation to apply before use

      • Value (float) : Final
    6. Translation_ID : Trans > pre-transform to apply, translation; same as for scale – could be standard and therefore spec not needed?

      • Value (3xfloat): Final
    7. Rotation_ID : Trans > pre-transform, rotation; same std issue as above.

      • Value (4xfloat): Final > (x,y,z,angle)
    8. RotX_ID : RotX > IF the model should be pre-rotated along the x-axis; again, should be std and be done with. Default is true

      • Value (boolean): Final
    9. FlipTex_ID : FlipTex > whether v coordinates of texture should be flipped (i.e. whether 0,0 in texture space should be top left or bottom left); default is false

      • Value (boolean): Final
  24. ModAnim

    1. Name_ID : Name > name is used as an id by CS… eg “walk”, “run”, “jump”

      • Value (text) : Final
    2. File_ID : File > this is a .caf file for now; should format be a deeper level?

      • Ref (text) : Final
    3. Type_ID : TypeAnim

      • Value (uint8) : Final > idle, travel, cycle, action, none, style_cycle currently from CS&PS – WTF?? do we even want such a thing?
    4. Vel_ID : Velocity
    5. Time_ID : Interval > this is “interval for idle or override actions”, in seconds

      • Value (2xfloat): Final
    6. Idle%_ID : Idle% > this is “probability (as %) of this action being the overriding action”

      • Value (float) : Final
  25. Velocity > this is for animations

    1. Base_ID : Base_Vel > speed of translation when animation is used on its own

      • Value (float) : Final
    2. MinVel_ID : Min_Vel

      • Value (float) : Final
    3. MaxVel_ID : Base_Vel > min and max are used by CS’s internal “blender” to get what precise speed is required at one point or another

      • Value (float) : Final
  26. ModMesh

    1. MeshFile_ID : MeshFile > format is .cmf ; should format be a lower level?

      • Ref (text) : Final
    2. Size_ID : Size > is this actually mandatory/needed here? Can’t quite yet tell.

      • Value (size3d) : Final
    3. Name_ID : Name > name of this part of the model, eg “Head”, “Neck”, “Left_Arm”

      • Value (text) : Final
    4. Material_ID : Material > CS allows several materials mixed on a mesh (though on occasion with this sort of thing it “allows” while at the same time complaininig if too many textures or too many this and too many that) but I’d stick to ONE material only.
  27. Mod3DGeneric > aka structure/item (non-moving)

    1. Material_ID : Material > while several materials can in principle be used, I’d rather stick to ONE material since *that* can combine several textures anyway.
    2. (Vertice_ID : Vertex)* > one vertex as a minimum

      • Value (position) : Final > this is given in model’s own coordinate space
    3. (Triangle_ID : Triangle)* > each triangle is given as (i1,i2,i3) where i1-3 are indices of the vertices given previously

      • Value (3xuint64): Final
    4. (Normal_ID : Normal)* > there should be one normal vector given for *each* vertex

      • Value (3xfloat) : Final
    5. (Colour_ID : RGBA)* > there should be the colour given for *each* vertex

      • Value (4xfloat) : Final > this means r,g,b and alpha channel
    6. (Texture_Map_ID : TexMap)* > there should be mapping from each vertex to a texture point (ie u,w coords)

      • Value (2xuint) : Final > these are coordinates in the 2d, discrete space of the texture
  28. Mod2D

    1. Texture_ID : Texture > NB: this currently is “texture2d” but it’s unclear if the distinction is worth much (ALL textures are anyway 2d images, ffs)
    2. Transparent_ID : rgb

      • Value (3xfloat) : Final
    3. Alpha_ID : Alpha > this is int in the current code, hm.

      • Value (int8) : Final
    4. Pos_ID : position

      • Value (3xfloat) : Final
    5. Size_ID : Size > height and width

      • Value (2xint) : Final
  29. Material

    1. Name_ID : Name > CS uses “names” in fact as IDs; so there can be several materials with same texture/file/data but different names…

      • Value (text) : Final
    2. Texture_ID : Texture > CS works with materials (it wraps any plain texture into a material anyway)
    3. (Shader_ID : Shader)* > a material CAN have several shaders applied.
    4. (ShaderVar_ID : ShaderVar)* > those are used *across* shaders to customise them, hence put here as separate, ugh; I think though that they belong more logically as a property of each Shader.
  30. Texture > CS supports: png, tga, bmp, jpg, dds, gif, mng, jng; common use given: png/tga for textures with alpha; jpg for those without; “best” = dds because it stores mipmaps directly + no need for additional steps (for a png there is uncompressing + mipmaps calc + recompressing to upload; a dds is directly uploaded to graphics hardware)
    > There are ALSO, “texture classes” but it’s totally unclear if they bring anything useful or if they are anyway a server concern at all; from the manual (p. 331/644): texture class = collection of settings controlling how a texture is uploaded to the Graphics hardware (e.g. lookup, normalmap, nocompress) + “adding semantics”.

    1. Name_ID : Name

      • Value (text) : Final
    2. Image_ID : TexImage > at this stage it’s unclear if stuff like size should be given explicitly since it should rather be either extracted from the file itself (?) or default/predefined (?)

      • Ref (text) : Final
  31. Shader

    1. Name_ID : Name

      • Value (text) : Final
    2. ShaderTypeID : Name > ugh; those are again predefined CS stuff/available shader types…
    3. File_ID : File

      • Ref (text) : Final

      > filename containing the shader description; atm those are a bunch of xml files, full of dependencies & referencing one another + cs-specific.

  32. ShaderVar

    1. Name_ID : Name > NB: those names need to match what shaders (those xml files) know/expect, for anything to work…

      • Value (text) : Final
    2. Value_ID : Texture|Float|2xFloat|3xFloat > the value of the shader variable has one of those types

      • Value : Final
  33. Light > ALL lights are “dynamic” since they can always change/be added/removed etc.

    1. Name_ID : Name

      • Value (text) : Final
    2. CentreID : Centre

      • Value (3xfloat): Final
    3. ColourID : rgb

      • Value (3xfloat): Final
    4. AttenuationID : Attenuation > “type” of attenuation (?)

      • Value (uint8) : Final
    5. RadiusID : Radius

      • Value (uint16) : Final

As full of holes and possibly over-wordy and all that as the above draft finds itself, it was still quite the toil to extract. Nevertheless, extracted and written down it had to be as I couldn’t quite move any further without this step. Now that it’s pinned down, I can go through it and discuss the questions/issues that are still unclear throughout. There are also a few aspects that are currently still entirely missing (not that the rest is guaranteed to be complete/final), namely:

  1. Some meta resources – most notably authorship of art files – are still missing entirely. I’d rather discuss/iterate first those concrete (relatively) parts extracted from client-entrails before moving on and fixing the details of that, since it’s more likely to be actually easier anyway as it’s way more clearly and sanely defined to start with.
  2. ModAudio – the definition of this is still missing as I never even bothered yet to get the SoundPlugin to work at all. Perhaps it can be added/fleshed out later on and then clients that know it / want it will use it, while the rest will simply ignore it as an unknown/un-usable part.
  3. ModEffects – this is also still missing but unlike the audio part, this could be more readily investigated and fleshed out more, by figuring out how to load/create effects from code directly, similar to the previous work on characters for instance.
  4. Water – this requires an investigation into creating water bodies from the code directly; specifically: some practical experience using CS’s procedural textures and some tests to see if/to what extent this could/couldn’t be folded into structure/item
  5. Weather – this requires an investigation into CS’s particle systems at the very least (for snow/rain/etc) and some more reflection + testing to check if it makes perhaps more sense to fold this into Air anyway.
  1. This is just one type, the name is given like this to make it clear that both “items” such as an axe or a flower and “structures” such as a house or a bridge are in here. Basically anything that is not terrain/water/air and can’t move by itself is in here (if it can move by itself then it’s in Characters) []
  2. NB: CS uses floats, with 1,1,1 for white and 0,0,0 for black.[]

September 5, 2019

A Summer’s Summary and Next Steps in Eulora

Filed under: Coding,Eulora — Diana Coman @ 11:34 am

Coming back from any holidays, no matter how short, means that I spend initially a few days simply with the task of picking up again each and every bit of work that I put on hold while I was away. Note that I didn’t say “dropped for the holidays” but rather specifically put on hold in a prepared manner so that picking it up again is not a horrendous chore but rather an opportunity to re-load the context with a fresher mind. And this makes it also a good moment to review the work done over the past months and state the next steps just as I usually do anyway for my own benefit before setting to do any work. The only difference this time is that I’m publishing here this whole summary of work done + work in line to be done next, so that anyone interested can follow along. Why wouldn’t I publish it anyway?

May was spent on two main pieces of work:

  1. A working initial 1 implementation (both on client and on server side) of the SMG protocol parts that are already clearly defined, namely network communication, account request/creation, serpent keys request & management, file request & transfer. This part meant Ada implementation of the protocol but also wrestling the legacy Eulora client into using the network communication structures of SMG Comms as encapsulated in the standalone Ada lib called EuCore. It also included some server-side work so that the end result is proper communication: the client can now ask for a new Eulora account and the server will reply according to SMG Comms; the client can also use its already established Serpent keys to communicate with the server and the server will reply accordingly. As required part of this work, a broad mapping of the client and its dependencies had to be done as well and basic docs for the client were published.
  2. The initial design of client logic for communicating with the server i.e. moving up on to how to use the messages that the protocol (and the implementation at point 1 above) makes available. This involved more reading, thinking, discussing and structuring than coding. The concrete result is a clear client strategy for knowledge acquisition and a very basic initial skeleton of its implementation. However, as the *structure* of the knowledge to be acquired was not yet defined or even fully clear, the next steps (see June below) had to focus on clarifying that, before the implementation can be pushed any further.

June started with fleshing out the hierarchy of data in Eulora as an initial draft capturing the relevant knowledge so far. This helped clearly identify the big areas that required further detailed investigation, most notably the graphics part where neither the required building blocks nor the specific client approach were yet known in enough detail to allow any meaningful attempt to extract a relevant data hierarchy. So the rest of June was spent on figuring out the graphics part: acquiring a better theoretical understanding through study of the reference book for computer graphics 2 and at the same time acquiring also a very practical knowledge of how things are done by first mapping broadly (and then ripping apart) the existing approach as it found itself spread and scattered through the legacy client, several plugins of the same and the several plugins more of CrystalSpace (CS) + the code of external libraries (cal3d especially). By the end of the month, the better understanding of the whole graphics part and the practical experience with it resulted in a working initial example of loading some specific type of graphics object and a clear structure of the notions involved. This structure doubles as the map to follow for the next steps towards making the client able to load everything on demand.

July focused entirely on graphics in Eulora, figuring out one by one and implementing a prototype loader for each of the sections in the map written at the end of June:

  1. Loading a static (incapable of movement) object given through its list of vertices, normals at vertices, colours at vertices, texture mappings at vertices and triangles that make its faces. This includes already loading factories and textures.
  2. Loading the landscape aka loading the terrain given through its heightmap and material palette. This includes also loading lights, materials and shaders. The whole was pursued only to the extent that the terrain and materials were indeed loaded as specified by the heightmap + material palette but without fully aiming to solve other potential issues such as the “mirror” effect that is more related to lighting and – possibly – some material or shader properties.
  3. Loading a character made of several parts that can in principle change on the fly. This part was pursued for now only to the extent that the character is loaded and shown + answers the player’s control but without fully going into the details of equip/dequip, actions and the full extent of changing bits and parts.

August was a bit split, with the holidays in the middle:

  1. At the beginning of the month, I continued the work on graphics but moved from the 3D issues to 2D, namely the user interface with all the different icons and images and various bits that are sometimes known as the “skin” of a client. This required a deep dive into yet another plugin of the legacy client, the widgeting PAWS.
  2. At the end of the month, I started working on writing down the 2nd draft version of the client data hierarchy integrating the results of all the work on graphics + all the pondering and figuring out done throughout the month + the further discussions and clarifications in the SMG boardroom on the desired working of Eulora’s communication protocol. This is still in the works currently but I plan to publish it for discussion as soon as I’ve got down all that I have so far and certainly no later than the end of next week (ie no later than 15th of September).

The current plan for September and beyond is this:

  1. Write and publish for discussion/revision the deeply revised (re-written really) draft of client data hierarchy. Deadline is 15th of September.
  2. Pending feedback and discussions, possibly dive again for more details if needed and/or revise further the data hierarchy, if/as required. This is a crucial part for moving forwards but it’s also one that once fixed will be very costly to change.
  3. Work on the implementation of the client and server-side pieces of the SMG protocol that are still missing, as soon as they are set/clarified. At the moment, they are listed broadly and in this order: client-side data storage (aka cache, of which currently there is only a skeleton, pending better understanding of what the data is going to be exactly), object request/reply, client actions, full client logic for knowledge acquisition and storage, full client loading/use of local data cache, client and server management of own keys.
  1. to be refined/reviewed as other parts such as key storage and bootstrapping become clear[]
  2. Foley, J.D., van Dam, A., Feiner, S.K. and Hughes, J.F., “Computer Graphics: Principles and Practice”, 2nd ed., Addison-Wesley[]

August 6, 2019

The Widgeting Paws of Pointerism (Notes on Graphics in Eulora, V)

Filed under: Coding,Eulora — Diana Coman @ 5:02 pm

It took indeed more than a week and I’m rather surprised it didn’t actually take even longer than two weeks to get *anything visible* out of it. It’s all one strand like Stan’s spittoon after all, so there’s rarely just “one sip” that doesn’t bring in the whole rot with it and I have to fight over and over the urge to just burn it all down and start a sane thing from scratch 1. Nevertheless, a lot of deep breaths and assorted cussings later, here’s a brief summary of the main achievements, in the order they were made:

  • some useless crap was removed and the whole thing made to work without (e.g. the doll with its dependence on a special sector in the map file);
  • the initial loading and “pre-caching” of meshes, textures, materials, maps and 2D stuff was excised (there are still the shaders remaining though as well as 1001 “preferences” in as many files in yet as many different places, sigh);
  • the actual handling of 2D icons and GUI images in general (e.g. buttons, borders, arrows, window decorations) aka the PAWS was dissected and exposed and roughly mapped as ugly as it is;
  • as recorded proof of breaking PAWS and because of its shameful lack of proper way to load images on actual demand, the whole thing has had a run with the stupidest “mouse pointer” available in there and otherwise with absolutely no 2D images at all for the very lulzy effect that it even looks better in some ways than with all of the various pieces of “art”, here it is:
    That snot on the screen is the mouse pointer on the black background starting screen.

    That snot on the screen is the mouse pointer on the black background starting screen.

    PAWS's shame: the better-looking interface with no images whatsoever (clicking the invisible widgets still works!)

    PAWS's shame: the better-looking interface with no images whatsoever (clicking the invisible widgets still works!)

The path that led in the end to the above started with me looking – as initially planned – at how the character (as moving sprite) is handled or rather mishandled, consider: there is the CEL layer that supposedly provides on top of CS the sort of structures needed by games to store and manage the various entities and objects; PS however “uses” it in principle but in practice it still mainly does its own thing anyway (and by now I strongly suspect that this pattern is simply because nobody ever spends the time to understand what they use); specifically, the PS way is to have a pscelclient class that supposedly manages game entities on client side (shouldn’t CEL have been enough for that, you ask? most probably yes but that’d have taken someone to figure out first exactly how it works and who has time for that!!); but then there is *also* a pscharapp class that handles the appearance of the character (apparently that’s too low stuff for “management” to handle) and there is also a psclientchar that moves about equipped items because this is yet neither management nor appearance but absolutely something separate – though you’ll be hard pressed to notice much separation in the code for all this theoretical separation; and to top it all, the inventory and doll views that precisely reflect character’s appearance and equipped items are actually the domain of something else entirely – the PAWS; and the PAWS (which is supposedly so separate as to bother to be a plugin by itself) in fact messes about with pretty much anything and everything from the engine to shaders and textures and 2D images (which are still loaded as… textures, but those are textures-this not textures-that) and xml-defined maps and hardcoded paths to all sorts of files it expects – nay, demands – to be present and just-so and at-start but oh-so-configurable. At which point it became clear that the PAWS mess is indeed such a large part of the whole mess that I’ll simply have to get to the bottom of it just in order to move further at all.

Looking at PAWS more closely revealed that it’s meant to be a GUI system. This initially rather puzzled me because there IS in fact already another GUI system in there, one that PS uses/relies on as a dependency, namely CEGUI. Then again, the PS style is to “use” something by simply sitting on top of it and mushrooming a lot of managers with hardcoded paths and expectations that they know the whole possible world upfront so there should be no surprise at PAWS, which is precisely this sort of thing – it mushrooms widgets and pointers rather than something-else and pointers, that’s about all the difference from all the other PS masterpieces.

Masterpiece aside, what IS this GUI system PAWS? As far as I can tell, it’s a lot of widgets so well organised that there are no less than 3 methods “FindWidget” because the 1st one (defined in pawswidget) checks the current widget and its children only, the 2nd one (in pawsscript) checks also the parent of the current widget while yet the 3rd one (in pawsmanager because yes, of course there are all sorts of managers) simply delegates it all to “mainwidget” aka a sort of calling the 1st one from the top of the tree (but no, they couldn’t possibly notice that it’s meant to be a tree and there are well-known algorithms for searches in a tree). Anyways, the fun part with widgets is the way in which they are defined really as each widget has an .xml file (in data/gui) that supposedly defines appearance only (though more concretely it’s about which elements it contains and on what position) and a .cpp file (in client/gui usually) that supposedly defines behaviour. The funny bit however is that both of those, for all their pretense of clear separation (they even are in totally different dirs, yes?), quite directly reference both one another’s bits and pieces AND global, all-of-a-sudden-made-hardcoded parts: the appearance references by name other widgets (that it uses for specific roles and in specific places so it’s not just a name really) and “resources” aka pre-loaded images; the behaviour code in turn has to reference various components of course. And in all this, apparently nobody noticed that before you can talk of behaviour and of appearance, there has to be somewhere a *structure and nature* of the thing that actually is the core and therefore the driver of everything else… Then again, what structure and nature and such silly notions, when the whole thing is bent on defining anything and everything by its current, temporary position or at best some role it might fulfill for a while 2.

The widgeting PAWS further has all sorts of notions regarding appearances: there are “skins” and “styles” and “resources” and they are all both hardcoded and expected to be fully known upfront. The Great Paws knows not only all the world that is but also all the world that could possibly be (the world as it defines it is all nothing but labels anyway so it makes sense in a way). And it enforces this by whining and even wagging its finger at the user if it ever finds that a file with what it *knows* is the “right” name is missing from its expected place, quite literally giving orders to the user, as the message spit is this:

“ART ERROR: PawsTextureManager loaded the image %s which was missing from the imagelist.xml and was loaded on demand. Add the image there!”

I’ll add to the above: and blow your nose and stand up straight and cut your nails shorter! Imagine that, being such a shitty user that the poor, poor PAWS had to actually load an image on demand (how degrading!!), you nasty user, you! And note that this “on demand” is anyway the sort of “you demand what I’m willing to do or no dice” not any sort of actual demand: it only means it deigned to look (because it had to use that image) in the *same place* it looked initially (when it tried to load everything that there should ever be) just because you are the sort of shit that failed to put the image there when it looked the first time, you know? What sort of user does such a thing to a poor piece of code, you tormenter, you, shame on you!!!

Before you get your hopes up that there is at least some sort of “loading on demand”, bash those hopes one over the head: for one thing this “on demand” still relies on the predefined list of what there *can* ever be so that if you request a “resource” it doesn’t know about than tough luck, it won’t try to look for it, it’s not that stupid to do something useful; for another thing, there is no way to actually call this on demand: it’s just a way of doing rigidly the same thing but expecting a different result because meanwhile the world should have better come to its senses and fit the expectations already, what.

If the above sadness is not enough, add to it this significant bit: PAWS considers itself a plugin and as such entirely separated from the actual client and therefore unable to use whatever the rest of the client knows. Sure, in practice this separation “works” by means of the main client class simply passing pointers to PAWS on creation – why use CPP if not for pointerism, after all. Moreover, the added benefit of this separation is that PAWS also gets therefore to have its own set of repeated XML parsing of all sorts since it can’t use the already-repeated XML parsing that the client anyway suffers from. And finally, it makes somehow perfect sense to “separate” the main client from the GUI system seeing how they both use the same graphics engine anyway and moreover the main client loads some images as textures while PAWS loads…well, some images as textures, yes. But they are not the same “class” of textures, so that’s significant separation, see?

Besides deep red, I tell you what I further see: since I have no intention to pass even more pointers on or otherwise to force this “plugin” nonsense on everything just for the very specialness of PAWS, it’s way more likely that the paws will be clipped more to size and therefore will take their place as yet another of the many classes inside the main client, possibly together with all the rest of very-special-plugins if need be (of course the dependencies are not that simple). I’m still pondering this, mainly because the idea is to touch as little as possible the code rather than as much as it takes to clean it (mainly because clean it won’t be anyway). But one way or another, I will have to find some reasonable way to bring PAWS in line with the rest and have it go and load resources from wherever (and whenever) it is told to. This might even require more discussion in S.MG’s boardroom but at the moment and as a result of all the adventure above, I have at least a much clearer idea as to what the 2D part is all about so there is therefore a point to even start the discussion from.

  1. While it might *seem* faster that way, it’s not, nor does it really make sense if one calmly considers everything involved. Urges like this are simply focused on getting rid faster of the current set of problems and do not care nor stop one second to consider the alternative set of problems that are bought by such “solution”. So yeah, urges make very poor guides, how surprising.[]
  2. And no matter how much I try to *not* see it this way, it always is the case that the closer one gets to the “how things look” and therefore in this case to graphics, the more confusion and more monkeying there is.[]

July 24, 2019

Naked Models (Notes on Graphics in Eulora, IV)

Filed under: Coding,Eulora — Diana Coman @ 11:30 am

What’s the first change you’d do to an empty-headed character such as Testy? Why, chop some bits off, change his sex and get him naked at least, even transparent at that, since he’s pretty much just as tedious and boring as all children dolls 1. Of course the first attempts failed to produce anything visible because it turns out that using Cal3D sprites in Eulora’s client goes through several layers of inmisdirection: Crystal Space (CS) wraps the Cal3D code in its own SprCal3D plugins and factories and whatnots; on top of that, there is also the rather elusive Cel (Crystal Entity Layer) that is meant to be a game-specific part of CS keeping track of game entities; and messing within and across all is Planeshift’s (PS) own GEM concoction interlaced with delayed loaders and Dead-Reckonings 2 and cloning of materials (why? no idea yet) and surprising, undocumented transformations. Still, after a while of hacking through all the above, I could at least get some parts of Testy showing… if lying down rather than upright:

Spot the bottom in the grass!

Spot the bottom in the grass!

The prostrate position makes at least sense in that “movement” also looks a bit like some swimming look-alike: parting the legs and advancing sort of thing. Then again, it turns out that using a different model (e.g. Cally) makes it even more interesting – the model is upright when waiting but it dives straight back to the horizontal as part of moving and it furthermore cares not one bit for whatever movement animation one loads. So there’s a clue for another day – current movement needs a good bashing over the head and a deep dive to figure out what’s all this nonsense. But until then, here’s naked Cally who borrowed Testy’s skin for a spell:

What bits is she missing?

What bits is she missing?

Anyway, since position of the model is always a matter of applying one transformation or another, I went ahead and applied the obvious transformation, 90 degrees and upsy daisy on her feet:

Upsy-Daisy with muddy skin!

Upsy-Daisy with muddy skin!

And funnily enough, once the 90 degrees rotation is applied to the factory (i.e. presumably to ALL entities built out of that factory) the Testy model remains upright even during movement without any perceived change. On the other hand, the Cally model is upright without the 90 degrees rotation but nevertheless dives down for movement and pops back up as soon as movement is done. So it would seem that movement has its own assumptions that it doesn’t bother to make public, how lovely. Seeing how it’s most probably to do with the model’s initial position or similar, I might even need to end up again with the full environment, Blender and all installed to get to the bottom of it and probably see exactly what or how the exporter is also broken and specific. Such joys to come but for the moment they are not yet here since the more pressing need is first of all to disentangle some more the PS+CEL+CS+CAL3D mess regarding animated entities so as to actually be able to *use* the walk/fight/move animations rather than just “add” them and moreover to be able to add and remove on the fly as many and as diverse entities as wanted, beyond the “main character” guy. And all this is getting deeper into PS swamps so it’s likely to take more than a week indeed – ideally by then I’ll be able to cut off at least some parts of the existing code (precaching and preloading especially) and lighten the client start a bit if nothing more.

  1. Changing graphics is this exercise in tediousness and pointlessness, it eerily reminds me of playing with dolls, just one step lower than that since those are just images of dolls, fancy that. And playing with dolls was utterly boring even when I was 5! It’s like an endurance test along the lines of watching paint dry. Tinker with this and tinker with that, all for “the looks” of it and can’t even satisfyingly throw it at anything to hear it break.[]
  2. Not kidding, although it’s way less exciting than the name they had the chance to land on: it’s mainly looking to see if your character falls down and breaks their neck.[]

July 17, 2019

The Mirror Land (Notes on Graphics for Eulora, III)

Filed under: Coding,Eulora — Diana Coman @ 10:26 am

Getting some actual landscape as in getting the height right turns out to be only half (the reasonably well-working half) of the terrain task: once I had in place at least the generic mechanism for creating factories and meshes, the terrain factory required only a few iterations, false starts and mandatory dives into CS’s entrails to get working and save Testy from his usual fall into the sky-sphere:
Terrain generated from heightmaps only (no mix of materials, only base material).

After setting up the lighting a bit better too, it looks even more reasonable (next to the crater that is currently empty, yes):
Base terrain from heightmap.

The truly frustrating bit turns out to be the painting of the landscape in the desired materials. For the relatively easy part, the Terrain2 construct of CrystalSpace provides a “terraformer” that can be fed a “material palette” (i.e. a set of materials to use) and an indexed image – as a result, the terrain will have a reasonable mixture of the materials depending on heights and an ok fading of the materials into one another where they meet so it doesn’t look that terrible, especially once all normals are in place too so that lights stand a chance to work:
Terrain from height map *and* material palette with 3 materials: grass, sand and rock.

The trouble however is that so far I haven’t quite managed to get to the bottom of turning down the reflective mirror-like surface of this landscape! In theory, materials have those 2 light-reflecting properties, namely the extent to which they are “diffuse” or “specular” (i.e. “shiny”). Still, for all my futzing around with those for all three materials (sand, grass, rock), the observed effects are pretty much non-existent. So much for the theory and welcome to this graphics thing of tinkering. To see clearly what I mean, here’s a rather surreal rendering with the ambient light turned on to higher levels so it’s more obvious (note that the effect is precisely the same even with low ambient light – it’s just harder to realise what it is exactly; similarly and as expected, one can mask the effect to the point that “it’s fine” by turning on the fog button – it makes sense since it dulls the ambient light, I’d say):
mirror_land

On the positive side, the futzing with lights and whatnots is really something of less concern for me at this moment since I’m not that interested in obtaining an image *just so* – my goal is in figuring out *what* sort of things one can set, what do they do and how to set them while the game is running. In this sense, at least the basics of the terrain seem to be in place for now: heights are read from a heightmap file, the materials are indeed used according to heights, the rest is for another day. I suspect though that in usual tradition, the only real solution here is to dig in the end in that shaders pit as well since it’s most probably a shader thing: the terrain object uses a different, terrain-specific shader and for some reason the defaults for that end up producing more of a mirror than a soil, at least given *everything else* (such is graphics, everything and anything can be the culprit). Note that I specifically used and set precisely the same parameters for materials as they are in the client currently in use – so it’s most probably something else/somewhere else that I’m not setting just right for now.

The next step now is to figure out animated meshes too, hence Cal3d objects. So in the next episode, it’s Testy himself that will change and get created directly from code. Once I have at least one way to do this too, I can go back a bit and look again into integrating them into (or extracting them out of…) the rest of the PS infrastructure. Then again, before doing that, I might still need to dive in and figure out the shaders in more detail to extract some way of specifying them without xml and directly from code too, perhaps. That promises though to be a lot of figuring out and additional mess so depending on whether it can or not wait a bit longer, it might not be exactly next in line – basically I have lots of work competing for my attention, a sort of inverted looking-for-work, it’s the… (lots and lots of) work looking for me!

July 8, 2019

The Rocky Moon (Notes on Graphics for Eulora, II)

Filed under: Coding,Eulora — Diana Coman @ 9:35 pm

My client-side Eulora task set last week is completed and as a result, the Testy character gets now to see something more than just fog in his Sphere-of-all-beginnings, look:

Testy under a Wavy-Blue Moon, in the Sky-Sphere of all beginnings.

Testy under a Wavy-Blue Moon, in the Sky-Sphere of all beginnings.

It turns out that CS can indeed eat – though not very willingly 1 – “geometry” for fixed/static entities (aka “generic meshes”) even without any xml in sight 2. For static aka non-moving entities, there are 5 sets of numbers required:

  1. Vertex coordinates
  2. Normals at vertices
  3. Colours at vertices
  4. Texture mappings at vertices
  5. Triangles

The main trouble with the above was the exact “how” and “where” to actually set them so CS understands what they are. It turns out that the most obvious “those are the vertices and those are the normals etc” approach is “obsolete” and replaced apparently by having to specify a render buffer for each set of numbers. Of course it’s still the same arrays of numbers either way, but with render buffers you need to also specifiy which *type* of render buffer you have in mind (i.e. what are those values for) and in pure CS style this is specified by giving the… name. Name that is afterwards internally translated into a numerical id of course but nevermind since it’s supposedly as easy as it can get to have to remember that the name is exactly “position” and not “positions” nor “vertices” nor anything else 3). So easy in fact that I ended up having the list of “buffer names” at hand all the time, great help indeed.

Before moving on to some basic descriptions of those 5 sets of numbers, it’s worth noting that they are just the most usual 5 sets really: there are plenty others that one can specify though it’s unclear exactly when and precisely why would one do. The easiest example concerns texture coordinates: one can specify as many as 4 different texture mappings. And why stop at 4 if one went all the way there, why not 5 or 6 or 7 or 1000? No idea. Anyway, for the basic descriptions:

Vertex coordinates are exactly what the name says: the 3D coordinates of vertices that define the entity’s shape in space. Those are all relative to the position at which the entity is drawn at any given time, of course, since they are internal to the entity and unaware of anything else in the larger world.

Normals at vertices are perpendiculars on the surface of the entity at each of the vertices defined earlier. Those are quite important for lighting (together with the orientation of each triangle surface defined further on).

Colours at vertices specify the colour of the object at each vertex and allow therefore colouring of the full object through interpolation for instance. While in principle this can be skipped when a texture is given, the “skip” simply means default colour (black) so it’s a “skip” only in the sense that it’s less obvious it exists.

Texture mappings at vertices – those are tuples (u,v) that specify a texture point (2D because textures are 2D images) that is to be mapped precisely to each vertex. Just like vertex coordinates provide fixed points for the shape of the engine that is otherwise interpolated at non-specified points, the texture mappings provide fixed points for the “painting” of the shape with a given texture.

Triangles define the surface of the entity as approximated through triangular shapes. All surfaces of static shapes in CS are approximated through triangles and the only difference between curved and flat surfaces is essentially in the number of triangles required so that your eye is fooled enough so you like rather than dislike the lie. This is after all the whole computer graphics in a shell: a lot of effort spent to lie to you the way you like it.

Triangles in CS are given as triplets (A,B,C) where the values are simply indices of the vertices previously defined. So the simplest cube will have for instance 8 vertices for its 8 corners as well as 2*6=12 triangles for its 6 faces since each face, being a square, needs 2 triangles to approximate. And those 12 triangles mean 36 values in total since each triangle needs the indices of all its 3 vertices. Moreover, the order in which the vertices are specified matters as it gives the orientation of the approximated surface 4 and that in turn is crucial for calculating lighting (and ultimately for figuring out whether you get to even see what is on that surface at all). Since it’s not intuitive perhaps, it’s worth noting the basic fact that a surface has 2 sides and the “painting” of a texture is strictly on one side only. So if you mess up the surface’s orientation, you can easily do the equivalent of dyeing a garment on the inside without any colour whatsoever showing when you wear it unless you turn it first inside-out.

As you might have guessed from the above example with the most basic cube if from nothing else, even very simple entities require a lot of values tediously and boringly specified – hence quite the bread and butter of automation 5, yes. For now and for testing purposes, I simply extracted the numbers of interest from the xml description of the moon object in game and then I used them as default values for an object created directly from code. And lo’ and behold, Testy got a moon to stare at and the moon even got to paint itself stony when I’m bored of its usual wavy blue:

Stony Moon and Testy in the Sky-Sphere.

Stony Moon and Testy in the Sky-Sphere.

The next biggish step further in this line is to figure out how to generate terrain from just a heightmap and then how to create an animated (hence, Cal3D) entity from code rather than through the sterile xml-packaging. Looking even further from there, monstrous xml-shaders await in the same direction but in other directions there is still a lot of work to do on teaching the PS code to use the EuCore library and to discover as a result that the world is way larger than what fits in its inherited, well-worn and time-proven, traditional xml-bible.

  1. A bit like those finding out that they can actually eat food that was never packaged in plastic or washed in chlorine or even re-heated.[]
  2. At least not in geometry-sight. In a bit wider perspective, all shaders so far are still balls of xml that will probably have to be untangled sooner rather than later.[]
  3. If you give a non-existent name, the buffer is simply ignored since supposedly you can define your own buffers too so it’s not an error. Combined with the wonderful way in which everything influences everything else in graphics, you get – if you are lucky – to puzzle over some unexpected visual effect that might even seem at first to have nothing at all to do with the names of the render buffers at all (maybe you messed up a few numbers in that long list, you know?[]
  4. By convention, vertices are given clockwise as read when facing the surface[]
  5. Automation can be here exporters from supported tools such as Blender and generators of known shapes/patterns.[]

June 30, 2019

Notes on Graphics for Eulora

Filed under: Coding,Eulora — Diana Coman @ 9:31 pm

Eulora’s client uses the CrystalSpace (CS) engine for all its graphics needs and CS in turn uses the Cal3D library for animated objects in the world 1. Some two years ago 2 I wrote this stop-gap glue-and-string script to supposedly help artists and all those eager to contribute but held back by a lack of direct way to experiment and see their “art” coming alive in CS. Well, I wrote it and until the beginning of this month, those scripts plus some basic notions from introductory computer graphics courses taken way back during my uni years were about as much as I could say I actually knew regarding computer graphics.

As it seems quite conceivable by now that I’ll end up having to get all the way to the bottom of the graphics pit too, I spent most of this month reading the graphics reference book 3 and in parallel trying to make sense in a very practical way of the whole way in which Eulora’s client handles all the graphics part from the Planeshift (PS) top layer down to CS and CAL3D. And while I don’t yet have a fully working new pipeline for actually loading on the fly all and any sorts of graphics into Eulora’s client, I have at least a hard-won better understanding of what goes on in there (despite the layers upon layers of obfuscation) as well as some bits and pieces that might conceivably even make it into such a pipeline in the end. So I’ll jot down here my findings so far and those yet-moving ideas about it all, as it helps for sure to structure and unload a bit in preparation for the next stage of work. And it might even help – says ever optimistic me – to start perhaps a discussion with anyone who knows this better than I do so if that’s you, kindly comment below and let me know your thoughts.

One difficulty with CS (and it seems to me with Computer Graphics in general really) is this persistent idea of accomodating all sorts of “alternative” ways of doing things that turn out in fact to be precisely the same thing only packaged differently so that nobody is left behind or something. So instead of having a clear structure of knowledge to acquire, one is supposedly left to be creative and find what “feels natural” or “comfortable” to them. Well, so much for actually learning anything there, welcome to lots of code that does the same thing, only it’s harder to tell that upfront without sinking hours in it, to follow it all from one end to the other. Nevertheless, after all the reading and poking it and changing it and having my way with it, my current basic structure of CS 4 notions of interest for client graphics is this (ordered from top down, as much as I could):

  1. Sectors – those are the highest-level containers of “graphics” of any sort. A sector simply stands for a “location” that is effectively defined by nothing more than a name 5 and two mixed characteristics: on one hand the visibility culler in use (so a computational matter) and on the other hand the ambient light (i.e. the light that affects everything in that sector in the same way at any position and as such a matter of aspect pure and simple).

    CS supports two visibility cullers, namely Frustum and Dynamic (dynavis) with the default for the game’s needs being the dynavis culler. So a first building block for the desired client pipeline is simply a “GetSector” function. Like all the rest of the “GetX” functions that I have in mind, this will retrieve any information it needs about the sector from EuCore / EuCache (via a CPP-to-Ada layer, as needed) and then either retrieves the required sector from the engine itself if it exists already or otherwise proceeds to creating it and loading it into the engine so that at the end of it, one way or another, the sector is known to exist. When information is missing/incomplete/unavailable, the GetSector method fails and lets the caller decide what to do (try again at later time, most likely). At this moment I have a prototype of this method as well as some similar prototypes for GetLight, GetTexture and GetMaterial – in other words, I can create from code without any xml and parametrised four main things: sectors, lights, textures and materials.

  2. Lights – those are sources of light in a sector beyond and in addition to any ambient light. They can be customised in all sorts of ways (e.g. intensity, colour, position, direction). Whether they are elements inside a sector or an environmental characteristic of a sector is unclear to me at this stage since they could easily be seen either way. From an implementation point of view however they are currently clearly elements inside a sector, NOT characteristics. This means among other things that one could in principle add/estinguish/modify lights as they please at any given time and I’d say this is how it should be, given that it’s not inconceivable to be able to turn on a candle or any other light in one’s own house for instance.
  3. Weather – the more general name for this would be environmental factors I suppose, but so far it’s really at most “weather” and in practice mainly… fog. Supposedly one could add here all sorts of *effects* really so from an implementation point of view I’d even call this part “environmental effects” i.e. whatever effects manifest themselves in the sector as a whole and are specific to it. The fog itself so far appears to be a rather simple matter as it’s essentially a limited modifier of ambiental light. I don’t really know if this is just a limitation of CS or a more general approach and for that matter I can’t for the life of me quite grasp yet exactly how do graphics-people decide whether some effect they want is rolled into “fog” or into “ambiental light” or into anything in there. This is an issue at all levels and so far it really looks like an individual preference than a choice, quite everywhere. In other words a matter of “am I more skilled in making it look just so by tinkering with this or by tinkering with that”?
  4. Meshes – those are any and all elements in a sector that have what CS calls “geometry”. This geometry concept is a bit fuzzy at times but as far as I managed to pin it down, it would seem to mean essentially that the object has a shape defined mainly as a set of “vertices” aka points in the 3D space that the object occupies. There are 3 main types of Meshes:

    1. Movement-capable entities
    2. Fixed entities
    3. Terrain

    Each of the above types of meshes has its own specific requirements and even ways of defining itself, including its geometry (hence part of the fuzziness…). At least in the current setup and as a very brief overview, movement-capable entities are effectively CAL3D models defined as hierarchical structures of meshes with attached action representations (e.g. run/move/jump/fight animations) and skeleton-based geometry; fixed entities are CS’s “generic mesh” with vertex and triangle-based geometry; terrain entities are CS’s “terrain2” mesh type with attached terraformer for generating the geometry based on a heightmap. The details of each are much gnarlier than this list might suggest and I’ll be teasing them out one by one as there is no way around that but at any rate, CS’s approach seems to have been to make different xmls for each of them and all sorts of “loaders” on top of loaders and on top of plugins, services, parsers and whatnots. Then PS added its own loader on top of CS’s pile of loaders. As you might imagine, this tends to rather obscure the precise way in which one can create those different meshes *without* xml.

  5. Factories – those are abstract concepts containing effectively the blueprint for creating identical meshes. The idea behind them – as far as I can tell – is that it’s worth loading in memory what is needed for a mesh only once and then reusing it every time a new specific instance of that precise mesh is required. In practice I’m not sure that this is really worth much for anything other than snowflakes or raindrops really, since otherwise pretty much each instance of a mesh seems to have its own factory anyway. There is however the argument that the factory should contain only truly common structure (i.e. all players of one race have the same structure) and then leave the customisations to each instance. So keeping this in mind, here they are in the list, factories for meshes (of all sorts, though indeed, each of the three different mesh types has its own specific factory type on top of a generic type).
  6. Materials – as far as I can tell so far, a “material” is essentially any specific combination of one shader + a whole set of values for the shader’s parameters (aka “shader variables” in CS terms). Note that the texture itself (or themselves as apparenlty one can use several although it’s not clear to me WHY is that any better), if there is one, is effectively an argument passed on to the shader.
  7. Textures – those are the pretty picture that is lovingly painted on some/any surface that an object in the world may show to the player at some time or another. Essentially each object is but a set of surfaces linked together and each of those surfaces may be covered with one or another texture fixed with some spit so that it goes exactly over that bump or starting from that point to end up just-so. In principle, one could provide instead the way to calculate the texture at any point but that seems to be less frequently used in practice (CS claims to support it though, although I can’t tell to what extent and with what results really).
  8. Shaders – this is where the fuziness increases to madness levels. On one hand, “shader” seems to really mean all sorts of things in different places, from yet another description of a surface to any code running on the GPU. There is obviously more reading that I need to do on this aspect but so far what I’ve got is that CS’s “shaders” are really just another layer of encapsulation meant to be for types of surfaces. So if one has types of meshes encapsulated in factories, one also has types of surfaces encapsulated in shaders and is able therefore to apply some “surface type” to any given texture so that the same picture will look “cloth-like” or “stone-like” depending on the nature of the surface on which it is applied. This is about as clear as I can get it now and I can’t say I like it much. On top of this, shaders in CS are yet another pile of layers and layers of xml with intricate dependencies on one another and on who-knows-what-else in the name of reusability 6.
  9. Render Loops – those are (in usual graphics style as far as I can tell) simultaneously in parallel and on top of the “shaders” above. They are yet another encapsulation, this time of types of rendering + lighting objects. To make the difference between shaders and render loop abundantly clear, default render loops are of course provided by CS, as files in the /data/shaders directory. Admitedly I totally lack the expertise to say at this stage just why are BOTH shaders and renderloops needed as separate entities and/or why not either do with only one of them or otherwise merge them together in a single thing at least but hopefully one of my more knowledgeable readers clarifies this for me. Failing such clarification, I’ll need to keep on reading and experimenting with it, of course, for as long as it takes, what else.

In addition to the list above, there would be also the easier part of interface “skin” aka the variious icons and decorations for windows, buttons, labels and whatnots. This part is also on the list to be made fully reliable on data received from EuCache but so far I haven’t touched it yet (other than keeping an eye out for what I could do about it, whenever other bits and pieces make me go close to that part of the code that has anything to do with it, of course).

For a fresh image, the TestChar at least moved out of its cubic box and into a rather faceted sphere, in his quest for the promised infinite landscape. Lights got a bit better, shader variables were sorted so as to neither block nor directly crash CS anymore. Next on the list is fully figuring out just how to properly feed “geometry” to CS as what it is, namely several sets of numbers (vertices, texels, normals, triangles) rather than all those + a ton of xml on top. This is a required step in order to have the GetFactory prototype too for static meshes at least. Once that is done, the next step is to move on to factories for terrain and then for animated meshes. Until then though, here’s Test-in-his-sphere:
test_char_in_spheric_world

  1. For completeness, I should mention that CS has also its own native support for animated objects via 2 different sort of objects even but neither of those is as flexible and overall useful as CAL3D is. Therefore, I simply consider that animated object = CAL3D from now on.[]
  2. Already two years passed! And to think that I’m picking it up precisely from where I left it all this time ago, kind of boggles the mind.[]
  3. Foley, J.D., van Dam, A., Feiner, S.K. and Hughes, J.F., “Computer Graphics: Principles and Practice”, 2nd ed., Addison-Wesley. Yes, I’m aware that there is a newer edition of this book, supposedly improved, better, shinier, more fashionable whatever have-you. I’m also aware that it’s full of what can only be called advertising for Microsoft while being way lighter on what I’m interested in, namely precisely principles of computer graphics. So I’ll stick with this edition, thank you.[]
  4. Note that there is no desire to make all Eulora somehow CS-dependent as such but at this stage, I have to at the very least support CS as it were so I’m aiming for using CS as a practical example of handling client graphics, not as a driver of standards or content.[]
  5. CS uses names aka text/strings as identifiers to the extent that it *requires* those for ~all the Find functions at all levels. It *does* have its own internal IDs and it even exposes them if one tries really, really hard to get to them but they are basically not very useful otherwise and moreover they can’t even be set to match an external set of IDs (i.e. one can’t create an item with a specified ID.) []
  6. “Reusability” seems to mean that the original author can skip writing a few lines of code at the cost of nobody else being able to understand afterwards at all how the thing works unless they spend at least double the time that it would take them to write it from scratch anyway.[]

June 11, 2019

Eulora Client Hierarchy of Data Draft

Filed under: Eulora — Diana Coman @ 10:29 pm

This is a very first draft to help with the current work on enabling Eulora’s GUI client to make use of new information as it becomes available from the server rather than expecting everything to be fully known upfront (as it currently does, laugh you not!).

Eulora’s data is one big hierarchy with 3 main types of information: structure, description, representation.

  • 1. Structure (i.e. what entities are inside what other entities)
    Structure is always a tree of entities where each entity is given through ID (unsigned 32 bits) + Position (x,y,z,rx,ry,rz):

    The Root of the world will ALWAYS contain:

    • an *abstract object* with meta-info:
      • ID of “self” (SelfID)
      • ID of current location (aka “sector” or room or world or whatever else it is) (LocID)
      • List of Skills: list of (skillID, skillName, skillCategory, skillRank, skillXP, skillKnowledge) (?)
      • Game Date and Time (?) — is this a meta-matter or is it a location-property?
    • the *top-most ID* (aka topmost entity); NB: this is not always the same as LocID since character may move into a location inside another location, presumably.
    • from top-most ID down, EACH node will have zero or more contained entities (hence, child nodes given through their unique ID and concrete position within their parent).
    • NB: each node can also have in addition to its structure-children (hence entities), any relevant number of description-children (see 2 below) or representation-children (see 3 below).

    • 2. Description of what entities themselves “are” through their relevant properties

      ANY structure-node may further have, alongside its children, one or several of the following properties (given as a tuple property,value):

      • name
      • description (??)
      • (- equipment slots (list of (slotID, entityID) ?) — are those any different from “contained”? I can’t see a truly significant difference.)
      • stack count
      • quality
      • HP (hitpoints)
      • BP (bloodpoints)
      • SP (stamina points)
      • MP (mana points)
      • SpP (spirit points)
      • MSP (mana spirit points)
      • weight
      • bulk
      • weight limit (max weight)
      • bulk limit (max bulk)
    • 3. Available representation of entities (graphics, sounds, effects, maps etc)

      NB: in principle there can easily be a structure + properties split at this level too since a graphical representation may have its own structure (e.g. map defined as a hierarchy of contained terrain + geometry meshes that have in turn different submeshes with corresponding textures/materials). This seems rather unseemly though and it’s unclear if there really is a need for a hierarchy of graphical detail that is anything OTHER than hierarchy of world entities to start with. So perhaps this can be flattened so that representation of entities means just that: a list of relevant graphical characteristics of ONE entity.

      ANY structure node may further have, alongside its entity-children and its properties, one or several of the following representations:

      • Mod2D (aka 2D model; at its most basic, the filename of icon to show for this; tbd)
      • Mod3D (aka 3D model; at its most basic, spec for mesh including material/texture and sockets if any; tbd) ; NB: sockets allow graphical representation of contained (specifically “equipped” or “component”) entities and therefore they should probably be identified by “position” so that the entity “child” on that position will then give the actual representation shown to the user (?).
      • ModAnimations tbd
      • ModMap (aka map for this entity; at its most basic heightmap + size (or is this fixed?); possibly also a list of materials to use + corresponding density)
      • ModWeather (unclear yet if this is any different from sounds+effects really)
      • ModSounds tbd
      • ModEffects tbd (unclear yet if this is substantially different from animations+sounds)
      • ModSkin tbd (stuff like window borders and button colours)

      ANYWAY: GUI may request whatever it wants from local cache, regardless of cache’s own internal representation (just as cache may store data in any form, regardless of smg’s own description of hierarchy).

    TO DO: flesh out in more detail each possible representation, for GUI to ask & work with + get it to actually work outside of/inside the intricate mess of ps paws, widgets, managers and whatnots maggots.

May 27, 2019

Eulora’s Client Core: The Dedicated Requester

Filed under: Coding,Eulora — Diana Coman @ 9:38 pm

A crucial requirement of Eulora’s new client is to actively request from the server ANY data that it may find itself missing at any point in time. At first glance, this seemed to me simply a matter of providing request services 1 from Eulora’s new Ada-based core and then adjusting the existing C/CPP code of the legacy client to make use of those services. This rather optimistic idea is of course plain wrong: “adjusting the existing C/CPP code” in this context is similar to saying that one “adjusts” a sheep to use the library – while it can certainly be done for various definitions of “done” and the sheep may indeed use the library one way or another, it’s at best a huge waste (of time, of resources, even possibly of steak) for everyone involved and no matter how one looks at it.

Even leaving aside for a moment the trouble with “adjusting” the legacy tangle in any direction, the more important issue here is that this requirement is not as much a functional requirement as a non-functional, quality of service requirement: whichever part of the client provides data services, it should better be dedicated to the task and do whatever it takes to get it done instead of “providing” it only fair-weather style – if it’s easy, there you are and if it’s not easy then it’s your problem really. In other words and in marked contrast to the very democratic “best”-effort-you-can-do-anything-anytime existing C/CPP code 2, the new code should have a clearly defined task and then either complete it or die trying over and over again, taking full responsibility for the process involved, not just for some specific detail conveniently chosen nor – as an excuse for not delivering – for the outcomes that are not fully under its control 3.

Considering therefore “active and dedicated request” as a quality of data service on Eulora’s client side, it follows that its place is rather close to the data cache mentioned previously and at any rate inside the new Client Core since it’s certainly not some additional part of client logic nor some bit of user interface. However, I’m reluctant to make it the responsibility of the cache itself since the cache is a passive structure that focuses on *storing* data and *providing access* to it. Mixing passive data storage with active data acquisition doesn’t make much sense to me and even seems ill-advised for Eulora’s client given the competing requirements: on one hand passive, immediate-response local data storage and on the other hand active, possibly-delayed and world-facing (i.e. communicating with the server) data acquisition. So I’d rather avoid this passive-active construction and have instead the two as separate entities: a EuCache dedicated to storing and retrieving on demand *any* data; a Requester dedicated to acquiring *any* data that is demanded of it. Note that the definition of “acquiring” here has nothing to do with the means through which the Requester actually gets this data (specifically nothing to do with the exact messages sent/received/exchanged with the server). Acquiring some data means simply that the required piece of data becomes available in the local cache aka EuCache. So the Requester will keep requesting this data from the server through whatever means it knows until either the data arrives and becomes available from EuCache or otherwise the whole client kills itself for lack of server 4 and therefore of any possibility of playing the game.

Specifically, the Requester will be implemented in EuCore (and therefore in Ada) as a protected object exposing only a few procedures that are essentially notifications: some are notifications of demands for some specific piece of data (either an Object or a File really since those are the only 2 overall types of game-data that one can request from the server); the others are notifications of data being received or of timeout interval having elapsed (in other words a notification of failure to receive data). Note that the demand for an “Object” effectively means a demand of its properties and those might be indeed anything at all but each and every one of them will be either directly a value or otherwise an ID of another Object or of a File. All notifications (including those demanding data) are always accepted by the Requester but not necessarily immediately acted upon. Clients of the Requester do NOT control the actual requests to the server or messages exchanged and are not even concerned with those at all – the production of actual requests, their content and their timing are entirely the job of the Requester and under its sole control. Implementation-wise, the Requester will simply keep queues of requested Objects/Files and will then proceed as soon as it can to pack a request for as many of them as possible; this request will then be posted to the server and the Requester will set a timer to the timeout value so that in the worst case it is at least notified that nothing happened; when/if any data is received or when this timer expires, the Requester will check in EuCache to see what items (if any) of those requested are now available; any items that have become available will be discarded from the watchlist of the Requester (i.e. the demands for them are considered completed) and a new request may be created and posted to the server for any items that are still in demand but not yet available. Note that even in the event of a timeout, a “repeated” request to the server may not be identical to the previous request since the list of demanded data possibly changed in the interval.

One potentially iffy point for the Requester is its need to be notified of any incoming data. At the moment I don’t see any real way around this, short of making the Requester poll at set times the EuCache and checking if any data of interest has meanwhile arrived. I don’t really like this polling approach here because it’s rather wasteful without good reason: any incoming data is indeed received and processed by another unit that is also on the same level with the Requester, namely the Consumer (the part that processes messages from the inbound queue). So the Consumer will have to notify the Requester when new data is received. While several Consumers may be active at the same time (at least one for Serpent and one for RSA messages) this is not a problem at all since the Requester is anyway a protected object i.e. thread-safe. Note also that even if (some of) the consumers fail to notify the Requester of some incoming data, the whole thing will still work if only slower than it could: the timeout timer will wake up the Requester and the check of data will happen there at any rate. In other words, the Requester is capable of reacting to events if notified of them but not dependent on those notifications to do its job correctly.

Given its rather complex task, the Requester is currently on the top conceptual layer of EuCore, making use of quite a lot of other units from lower levels. Currently, the main relevant units on this top level are the following:

  • Data Cache aka EuCache – this is a passive, thread-safe entity responsible for storing all and any data given to it and retrieve or delete it on demand. As such, it *owns* the specific format in which data is stored 5 and it simply exposes functions and procedures for storing, retrieving, deleting and checking for data.
  • Communication Link aka Comm_Link – this is a passive, thread-safe entity responsible for persistent storage and updating of communication details, most notably RSA and Serpent keys for inbound and outbound communications as well as a message counter. This is effectively a specialized cache – where EuCache is for game data, Comm_Link is for communication protocol data. The requirements (including use contexts) and specifics of the two seem to me sufficiently different to keep them separate at least for now.
  • Consumers of incoming messages (RSA and Serpent) – those are separate, active tasks, responsible for processing incoming messages. Consumers own and get to define what “processing” means exactly but their role is to extract the data contained in the messages received and make it available for use by Eulora’s client. In practice this means currently that Consumers will pass any data received on to EuCache for storage and will notify the Requester of any data receipt.
  • Requester – this is an active, thread-safe entity responsible for acquiring data that is in demand. It owns the process of data acquisition from the server and it accepts any demands of data specified by some identifier. While it guarantees that all demands will be served at some point in time as long as the whole program is running, it does not (and can not) guarantee when or if the corresponding data becomes available. It can’t even guarantee that there IS any corresponding data and so ALL it can do is to guarantee that it will try with the same dedication for each and every bit of data to acquire it. Anyone demanding data can of course pester the Requester with more demands or give up or decide for themselves for how long they can or will wait.

And now that the main idea and overall design of this whole thing is at least quite clear to me, there remains of course the small bit of actually implementing it in full (rather than the current skeleton I already made) and sorting out the various implementation-level troubles that will appear as they always do, in all sorts of details. Relatively easy work compared to what follows it inevitably, namely teaching that C/CPP sheep to use the EuCore library…

  1. Mainly picking, packing and encrypting everything in the right format + sending it through to the correct place.[]
  2. Seriously, think of it: existing client code is this event-driven thing where *anyone* can subscribe to any event and then “do” anything at any time provided of course that the any and anything are in fact very narrowly defined and set in stone even to the level of names of various art files (it has to be a zoneinfo.xml inside this and that and put exactly there, sort of thing). If this narrowing of “do” was not a price high enough to pay for such code “liberty”, there is also the added reality of a huge tangle that does precious little indeed since everyone ends up calling anything from anywhere and no piece of code is really and truly responsible for anything bigger than a few variables here and there. And at the end of the day how could any code even be responsible for anything since it can’t *own* any process by itself (shared! event-driven!) and it has to be passive, mainly reacting to some events or at most… signalling through events of its own but never able to rely on anyone giving a fig about its signalling! So there it is, code – like anything you do – is more of a mirror than you might think. And “teaching people to code” has way more layers than “teach them Java” and certainly more issues than current “courses” even seem to be able to imagine.[]
  3. And now that it’s clearly stated like this, tell me first just how many people you know who actually handle their own work like that? And just what bit of “programming language” you think one needs to teach so that you get programmers to actually design their code this way?[]
  4. This “lack of server” is defined in practice as a number of successive timeouts on requests sent to the server, where the specific threshold value is chosen by the user via a config file.[]
  5. Currently game objects are stored in memory in Hashmaps aka associative arrays while art/files are stored directly on disk. Note however that any of this can be changed without touching anything outside EuCache as it’s nobody else’s business.[]

May 4, 2019

Eulora’s Client Core – Basic Docs

Filed under: Coding,Eulora — Diana Coman @ 8:56 pm

As I’m doing anyway the work required for a saner Eulora client, I’m stuck as well with writing a minimal documentation for it, since on one hand there’s no one else who can quite write it anyway and on the other hand there is no such thing at all 1 for the old piece of inherited gnarl so the slate is blank, the space infinite and the need impossible to know in advance. So I’ll just jot it down here for future reference and if you have anything to say about it, the sooner you write it down in the comments section below the better for you.

As general structure, the current plan for the new client is to have essentially 2 main parts: one is the actual Core, written in Ada and – hopefully 2 – provided as a standalone library, blissfully unaware of ANY of the C/CPP legacy tangle; the other is an updated version of the current C/CPP tangle of a “client” that will use the Core for pretty much everything except the actual GUI. The GUI remains provided via CrystalSpace and cal3d, as it currently is. In other words, the Core offers an implementation of a basic Euloran client, to be used from/by any UI, graphical or otherwise as you care to make for yourself. Note that the interface is NOT the only thing you can build on top of the client Core. Essentially any client logic, bots and anything of the kind would still be the sort of thing built on top of / around the Core. A quick diagram of the whole thing looks like this:
2019_05_04_clientcore_struct_narrow

The Core itself is currently made of 3 conceptual layers, in order from bottom to top: encryption utilities (EuCrypt), SMG communications (SMG_Comms), client data cache. The encryption utilities are those of EuCrypt: CRC32 calculation, Serpent encryption/decryption, Keccak encryption/decryption, RNG interface using Fuckgoats, RSA key generation, RSA encryption/decryption using TMSR OAEP schema. The SMG communication part provides mainly: UDP with fixed-size but configurable payload, in/out message queues and attached “consumer” tasks, config file reader, packing/unpacking (i.e. raw, bottom-most layer of SMG communication protocol), read/write of messages conforming to SMG communication protocol specification, thin layer on top for encapsulating a “communication link” i.e. required addresses, ports and keys for both ends. The client data cache keeps effectively the whole world of Eulora as the client knows it at some specific time and provides thread-safe getters and setters for anything and everything that it can possibly hold.

Note that the client’s knowledge of the world is by definition a cache as it flows always from the server and entirely in response to client’s own requests – there is by design no “push” of information or anything else from server to client at any time. So the client may request from the server anything and everything it wants to 3, having however to decide for itself what to make of the answers it receives and even what “obsolete” means in various contexts. At the moment this cache is still in the works and for this reason the least fully-fledged but at any rate, it has to grow around the structuring of data that is discussed in the SMG protocol specification: a potentially infinite hierarchical structure of “objects” 4 having each a subset of an overall set of potential “properties” 5 with their corresponding “values” 6. Due to this trinity of objects-properties-values with its corresponding trinity of ID spaces, I took to calling the whole thing a tricache but the name as the design are still in the works so they’ll have to enjoy their own detailed description at a later date. Until then, what have *you* been working on and how is that going?

  1. Perhaps you count this spaghetti piece as documentation but even so, just note who extracted it in the first place, anyway.[]
  2. I’m holding quite tightly to this idea as I certainly do NOT want to have it depend on any of the current C/CPP mess. This being said, I can’t know in advance what I might still uncover so for now and until it’s all done, the qualifier remains “hopefully” rather than surely.[]
  3. This in practice is of course quite severely restricted by what it *knows*, unsurprisingly.[]
  4. Where “world”, “map”, “actor” or “pot” are equally objects and not even the only objects. Essentially an object is anything that is present in the game’s world.[]
  5. A property is a pre-defined and public structure that specifies the *type* of values that make up the property and their actual meaning.[]
  6. The values may be given directly (e.g. “3” or “somename”) or by reference aka as an ID. This ID may be itself either the ID of a suitable object or the ID of a value that is essentially too big to pass around every time – in the protocol’s specification this sort of ID is currently called for historical reasons “filename” but it’s worth noting that there is no imposition for it to be stored as a file at any point.[]

March 3, 2019

Inconsiderate Considerations or a Tiny Euloran Dataset on Blueprints

Filed under: Eulora — Diana Coman @ 8:57 pm

Blueprints in Eulora are currently the product of expensive clicks: millions go in to pay only for the most inconsequential of tokens, thousands come out and few of the blueprints obtained are what one wanted to get anyway. In typical euloran fashion however, this exercise in the frustrations swamps is anyway on one hand unavoidable and on the other hand potentially, eventually, greatly rewarding 1 – with a bit or a bit more of luck and other assorted arcana. That being so, I let my luck do whatever it does and otherwise I set about to collect in one place a tiny bit of data on what comes out of where. About 200mn ECu later, here’s what I’ve got when clicking them with a noob 2:

Consideration Type 3 Consideration q 4 Bundle q Output q Blueprints obtained 5
10 x Apprentice Tinker Considerations 179 3700 6 34 LTF, Slag, CT, CC, PPB, IO, DUH, SCS, BCT
10 x Apprentice McGuyver Considerations 47 3287 7 17 QF, POC, Rockpile, CSW, GT, PC, CB, CDS, ETD, RH
7 x Neophyte Gumbo Considerations 152 752 8 14 NP, TT, FT, TF, WOA, BBB, ACG, BNG, CP, CF
10 x Neophyte McGuyver Considerations 2670 1838 9 91 SFH, IT, PS, MK, TM, BH, RH, CB, POC, HG

Conspicuously missing from the above, the very blueprint that I really wanted to see, namely BMS 10. The BMS would be in the McGuyver line but 10 Neophyte clicks and 10 Apprentice clicks failed to produce even 1 single bp. Rather sadly, the Apprentice McGuyver clicks still produced the useless Caveman Bedroll blueprints and other such shit (Reed Hauberk!) that one has too much of, even just from Neophyte clicks anyway. It’s totally unclear why exactly would some blueprints be SO common even though they are not necessarily the cheapest ones: take for instance the ton of LTF (4070 bv) or CT (1404 bv) blueprints obtained (0.1mn of each) and compare that with the 13 total BCT (540 bv) blueprints! If anything, it would be the cheaper blueprints that are harder to get but then again, I got tons of “Somewhat Fashionable Hat” and “Hoof Gloves” useless blueprints from the McGuyver line and those are the cheapest in that line (656 bv and 622 bv respectively).

Assuming that those rare bps aren’t simply unobtainable at this moment for whatever reason, the obvious conclusion is that those considerations are rather inconsiderate of the ~200mn base value sunk into them and won’t reveal in return even the very basic of *what* blueprints should one expect from where. Then again, it’s not *consideration* you want from a good game, is it?

  1. Different players might find different things rewarding but a basic reward would be the rare serious “popping” i.e. obtaining for once millions out of thousands rather than the other way around. []
  2. the ~only way I have to actually get a wider spectrum of bps since clicks with Foxy end up high quality output and 1 type of bp in the usual cases; in the rare case, Foxy can in principle get more bps too but here I wanted to see precisely WHAT bps one gets from where so I suppose it’s a gamble on the types more than the overall value itself.[]
  3. Each crafting line in Eulora has its own Considerations line. Each line has several levels of which so far there are three seen: neophyte, apprentice and journeyman.[]
  4. Quality of the Consideration blueprint used for this click.[]
  5. They are ordered based on quantity obtained, from high to low.[]
  6. ~=6.8mn base value.[]
  7. ~=6.05mn base value[]
  8. ~=1mn[]
  9. ~=2.83mn base value[]
  10. In the missing and becoming ever so rare range there would also be the CFT bp from Tinkering but at least I did not do a Neophyte Tinkering click this time…[]

June 26, 2018

Eulora’s Own CR50

Filed under: Eulora — Diana Coman @ 9:29 pm

Possibly similar to the CR50 item Stanislav has been torturing recently, a green “Microchip” of unknown internals and dubious usefulness has finally been teased out of the reluctant and downright hostile Euloran soil. It took only a bit more than 3 years of pounding said soil.

On the plus side, it seems to be quite valuable at somewhere around 110`000 ECu (aka 11`000 satoshi atm) base value. On the more usual *other* side, there is no known use for it at the moment 1 and it came out very low quality too, not to mention potentially booby trapped for all one knows:

Eulora’s own CR50! Microchip!!1

The floating Microchips!

At any rate, I hereby dedicate this very first lot of Microchips to Stanislav Datskovskiy. May all his quests be successful and take considerably less than it took me to find this item!

  1. It wasn’t needed until now, which is of course “no indication of future performance”.[]

June 10, 2018

Chasing the Elusive Euloran Quality

Filed under: Eulora — Diana Coman @ 3:45 pm

Quality of items in Eulora is a big thing – it brings in more money for whoever can deliver it and it also gives one better dings. Or at least better chances at better dings 1, it would seem. Welcome to the murky shores of Eulora, this land open to all and totally opaque to most 2.

The most recent update 3 was cheered for finally making it possible to actually do expensive stuff in a few minutes as opposed to a few months. After which there was silence and renewed confusion as to what is what and how much any darned thing is worth. Quite a lot of discussion ensued in the #eulora irc chan although mainly between me and Mircea Popescu as the rest of the veteran players 4 seem to either have no clue or have no appetite for talking at this stage. Hopefully they’ll find one if not both at some later point before totally sinking out of sight, unknown and unmissed for lack of actually being there, who knows.

While discussions in logs are all fine and good, they tend to be difficult to find and retrieve at a later time. So for my own future needs and potentially for others’ use, I’d rather jot down here a few data points on all this. The most recent problem is that apparently *everything* works now as a quality sink: mining eats up crafted items of 4k quality to give back harvestables at 900q while crafting wants also to eat harvestables at 900q to possibly give something back at all (not even clear if >900q, at that; a reported craft that “almost works” brings down q from 90 to 84). After all sorts of experiments with my team of noob and not-so-noob helpers, I suggested that people might simply be approaching the issue here from the wrong end, a sort of trying to dig their way out of the hole instead of making a ladder to climb back up. I know that a big-enough dig can indeed get one out of any hole through the other side, but Eulora’s rather short on shovels at this very moment 5. So how about that ladder then? It starts with more experimenting and concrete data, such as this, collected over only a few crafting runs of Slag (base value 677):

 

Tinkering Rank Sortage Rank Blueprint Quality Bundle Quality Output
1265 448 133 664 0 Slag + TPF q401
1265 448 67 664 0 Slag + TPF q285
1265 448 1 664 4-5 Slag + TPF q32
245 77 133 664 2 Slag q213 +TPF
245 77 67 664 3 Slag q151 + TPF
74 67 133 664 2 Slag q198 + TPF
74 67 67 664 4 Slag q141 + TPF

All the runs had remarkably similar results (same quality always, same number of slag items always except where it appears in table as 4-5 i.e. sometimes 4, sometimes 5) so I simply recorded one result for each combination. The bundles there are all identical and made by the highest skilled crafter as she can pack most value into the bundle. Specifically, each bundle is made out of 17 Flotsam q192 and 11 Shiny Rock 187q. For one thing, this suggests that the highest crafter packs the ingredients for almost 4 slags into one single bundle. For the other, it means that any output < 190q is anyway a sink of quality, no matter how one looks at it. And looking at it, there is some support on both points: 4 slags are reliably obtained by highest skilled crafter with a blueprint of lowest quality possible, 1. They are however q32 meaning that quality was severely lost in the process. When quality is not lost by the highest skilled crafter, it is however totally transfered to numina it would seem: all output is numina and no slag at all.

Considering the results of the other two crafters, the most interesting part is that they can actually get Slag of higher quality than the inputs that went in: for the same ~q190 harvestables used as input, they get reliably outputs >q150, even close to q200 in one combination! So for one thing it’s certainly NOT true that crafting is a sink of quality, quite on the contrary: it clearly creates quality of output – the only trouble is however to match the sort of output one wants (i.e. product not so much numina at the moment).

The even more interesting bit suggested by this teeny tiny amount of data is with respect to just how much of the actual value going in is then obtained back as Slag rather than numina. The input value is roughly 677*6.64 = 4495 ignoring the value of the blueprint (which is not much at 67*1.33 maximum in there). Highly skilled crafter gets precisely 0 in Slag out of this and all of it in numina unless they go for low quality when they get between 4*677*0.32=866 and 5*677*0.32=1083 so anyway about a quarter maximum. By contrast, middle level crafter gets 2*677*2.13=2884 or 3*677*1.51=3066 at lower quality so about 70% of the input value. And lower level crafter gets even more at their lower quality 4*677*1.41=3818 (or 2*677*1.98=2680 for their higher quality).

Before jumping to any conclusions, I’ll add that I’m quite sure that the data captured there (i.e. Tinkering and Sortage skill ranks, blueprint quality, bundle quality, item) is quite unlikely to really capture ALL factors that influence the outcome. For one thing I can tell for instance from long experience with those characters involved that the highest skilled crafter tends to be rather unlucky in everything she does, while the middle skilled crafter tends to be quite the opposite – lucky and then some more.

With the above caveats in mind and other potential limitations quite obvious 6, a conclusion is to be drawn and that is that at least on such low base value items beginner and middle level crafters are actually in a very good position to bump up quality and make a killing – if only they do buy the bundles of a highly skilled crafter! In turn, their output is likely to actually help highly skilled crafters as it gives them more quality as input for more valuable crafts presumably.

As for the highly skilled crafter, it would seem to me that she would really need to somehow pack in at least 4 times the value of Slag at her output quality of 401 in order to stand a chance of getting q401 Slag. That means she needs a bundle of 4*677*4.01=10859 so about 3 times more than what she made for this little experiment. Either she gets that from an even higher crafter (who can pack more of same q harvestables into one bundle) or she uses harvestables at quality at least 600 by the looks of it. Good luck with that my lovely, as q600 harvestables will come at a hefty premium at least from my wares – if they even come at all as it’s not all that clear at this moment that such stocks can be replenished if used!

Other than that: isn’t it marvelous how much one can get even from only a few numbers? If only they do bother to get those numbers in the first place, of course…

  1. The term of art in Eulora is “pop” as in “1002mn pop, wow!” i.e. you put in 10mn and got 1002mn out of it, you lucky bastard.[]
  2. That post was written in 2015, yes. Not much changed since then in terms of players’ understanding of Eulora’s actual workings it would seem. Poor Eulora, so totally incomprehensible, so entirely misunderstood…[]
  3. ‘Tis not even by far the only update Eulora went through so far. And all updates introduce changes that so far seem to mess up players’ previous plans something fierce.[]
  4. Noobs have yet some other things to figure out before standing much chance with this really.[]
  5. And since I’m not that young anymore in Eulora either, I can tell you that I’ve subsidized this sort of approach at least once before and I don’t want to do it again, ever. It was costly, very very costly.[]
  6. Very small sample is the most obvious but you really are better off thinking for yourself on this.[]

June 5, 2018

The Lack of Dust – A Very Euloran Problem

Filed under: Eulora — Diana Coman @ 6:23 pm

Collected Dusts in Eulora are a thing – a very useful and currently very much required thing. Consequently, according to the usual Euloran workings, Collected Dusts are also… rather sorely needed and totally missing from the market. In other words, Collected Dusts are hard to get, needed by the ton, while also unknown as to actual value – the usual stuff. And so Foxy set up to… get those 1, since she is anyway best at Lapidary 2.

At first, the recent update seemed as if it might help for once – first level numina such as Dusts are normally obtained as a byproduct of crafting in the respective line. That would mean that one gets tons of Dusts simply by clicking something in the Bouquinism line. And that is… true of sorts but the trouble is that Bouquinism is a bit special and most of its items give stuff-other-than-numina (blueprints mostly). Moreover, while the recent update seems to have turned skilled crafters’ clicks into mainly-numina-producing clicks, this does not really hold for Bouquinism! Click on Maculature and instead of loads of Dusts, one still gets loads of… Little Bits O’ Nothing. What do?

Well, whenever the rule doesn’t work, one needs to find… the exception, of course. For various definitions of find that might involve workarounds and workthroughs and workaboves and workbeyonds and workinbetweens – more generally speaking: WORK. So work it is then, onwards! In this case the work is mainly to figure out how to make mites in large quantities through lots of smaller clicks since apparently I don’t have enough input stuff to get more than a few mites through huge-quality single click sort of thing.

First, I tried all sorts of combination-clicking on Maculature in the vain hope that Bouquinism might still sort-of-work like the rest and spit some Dusts: high q bundle with total noob -> ton of LBN (Little Bits O’ Nothing) and nothing else; high q bundle with high q bp (recipe) and Foxy’s click -> ton of Nothing just the same; you name it, I tried it -> no, nope and nono.

Second, I made then some Bird’s Nest and Gin, poured it over that WPL 3 that nobody wanted yesterday but wanted today 4. That made the ECV 5 in tiny quantities and …DUSTS! In… some quantities because on one hand I did get a few thousands of them but on the other hand Foxy needs at least 3300 for one single Lapidary click that makes usually only about 20-30 Mites so by this measure… still totally and utterly stuck (and no, making some million of Bird’s Nest and Gin is not a useful “solution”). What do?

Third, I got a total noob to look at Foxy’s Toil recipes (the ones that make Mites out of Dusts). And what do you know? Noob can make a bundle with one single Dust and one single WOA (Water of Anamnesia – another thing that seems to be rather wanted and in short supply at the moment). So noob was set with the bundling bot to make a few hundred bundles that came out an actually reasonable ~1300 quality because Foxy’s Dusts are high quality and Foxy’s ancient WOA are ALSO high quality. Wonderful, except for a teeny tiny problem: clicking a 1300 quality bundle with a high quality Toil bp seems to end up for Foxy in… no Mites, only Gem Dusts (Lapidary numina). On the other hand, clicking it with the noob gets Mites, but poor, sickly and stinky ones – frightfully low quality, not to mention basically wasting all that Lapidary experience on the unsuspecting noob. Once again: WHAT do?

Fourth, got the big mixing guns out of Foxybot: blueprint quality doesn’t have to be huge if one also has 6 quality 1 blueprints of the same type! Some many clicks and 2000 mixings later, there we go, found some sort of combination that works! Foxy reliably gets at least 10 mites per click – not to mention many more when she pops or mini-pops – at a quality above 500 and she has *hundreds* of bundles and blueprints to click right now!

And the cherry on the above delightful cake is that it takes only 2 of those clicks for Foxy to rank up in Lapidary. Combined with the bot’s powerful crafting-with-bundle 7, she basically got 100 ranks up in one hour today and scooped up the book-prize for being first to reach 600 rank in Lapidary!

Onwards and upwards, let’s reach that 800 Lapidary before tomorrow’s update ’cause I’ll be busy mining: there are new problems to solve, troubles to find and generally millions of coins waiting to be picked up, scooped out, teased away from Eulora! If only, of course… you actually work at knowing how to do such a feat. Do you?

  1. By now I can say I have a thing for… prickly problems let’s call them, what can I do.[]
  2. The crafting line that works on numina such as Dusts, transforming it from lower levels to higher levels (e.g. from Dusts to Mites and so on).[]
  3. Worthless Putrid Leather[]
  4. Ha, HA![]
  5. Extremely Creaky Vellum[]
  6. by means of previous noob-clicking, at another time, there’s nothing totally useless if only done correctly in Eulora, you know?[]
  7. No, you don’t have this working anymore? Awww. The help and the code have been there for ages, waiting for… you . Where have you been?[]

May 24, 2018

A Tiny Stab at a Little Pricing Problem

Filed under: Eulora — Diana Coman @ 10:57 pm

To start off, here’s Foxy making disgusting goop in large quantities 1:

Why is she making that? Well, for profit, what else. With each click, she’s transforming 2852 ECu worth of rock-and-what-not into 4324 ECu worth of Disgusting Goop. That might not sound like much, except for the fact that it is effectively a 50% added value just like that in a few minutes (and while I sleep, yes, how else).

The above concrete numbers give me added ECu but also a way of approaching the ever-thorny euloran issue of pricing stuff. Only yesterday it would seem I failed at pricing some WPL correctly since the market (that had ASKED for WPL specifically) decided that… it was too expensive to bother buying it! So no sale made and a fee to pay instead 2 but also a very clear and concrete thing to point to next time when anyone asks for WPL or complains that they don’t have it.

Regardless of the market being there apparently completely lost in terms of what is how much and why would WPL cost more than dirt-cheap, lowest quality basic stuff, it’s still worth to look again at some fresh data and see what prices make sense at all. Taking the Disgusting Goop above, let’s calculate precisely what went in and what came out:

input: bundle of quality 595 + blueprint of quality 120 = 5.95*470 + 1.2*47 = 2852 ECu (quality adjusted base value 3 )

output: 8 DG of quality 115 = 8*1.15*470 = 4324 ECu (quality adjusted base value)

Output / input = 4324/2852 = 1.51 (i.e. 151% or 51% gain).

Considering that the DG bundle is made only from harvestables 4 it follows that Foxy simply can NOT sell those harvestables (rf, cr and sm) at anything less than 150% because at anything less than that she’d be better off using them as input to craft!  Notice also that the basic resources there (rf+cr) make only 2 thirds of the bundle, with sm the other third. For this little exercise I ignored the distinction between basic and non-basic harvestable but mining experience screams loud and clear that non-basic resources ARE harder to get in same quantities and same qualities. Which is not at all surprising given that they have higher value, doh.

As usual, in practice things are indeed a bit murkier, as all sorts of considerations quickly come stomping in: this 50% increase might be obtainable only at this specific sweet spot or only for this specific combination etc. Nevertheless, it IS some concrete data point in a sea of unknowns, so I’ll stick to it for now. I’ll add also that Foxy is a very skilled crafter (possibly even the top crafter at the moment) and it is for this reason that she *has this option* to make a significant profit just by crafting. Other players might find that the highest profit they can make out of harvestables really is by selling them at less than 150%, why not?

As Foxy’s rather busy earning about 1500 ECu every couple of minutes for now, let’s leave her aside for a moment and ask simply: what are *you* earning every couple of minutes and what are *your* options really?

  1. Yes, the red messages are all there one on top of the other because the bot is firing requests at the server without pause for clicking, eating, thinking or any of the other human-vices.[]
  2. There is a fee for auctions without sale to discourage spam of useless auctions.[]
  3. Also known as qabv and in any case the de facto *minimum* value of something in Eulora since that’s what you can always get if you sell the item to the merchant NPC (i.e. to S.MG directly).[]
  4. harvestables are obtained directly from euloran soil as a result of “mining” activity aka /explore + build; by contrast, craftables are items crafted as above, possibly from harvestables or other craftables[]

April 17, 2018

RFC: Eulora’s Communication Protocol (EuComms)

Filed under: Eulora — Diana Coman @ 2:59 pm

This is a request for public comment (questions, critique, discussion etc) on the current version of Eulora’s protocol for client-server communications that has been in works for a while now. Once this public discussion phase ends, the most significant aspects of this protocol will be effectively set in stone, since the procotol is central to any working client for Eulora. Consequently, your chance at having any significant input on it is *now* and only now. Any questions or feedback on it as well as any suggestions or requests related to it are most welcome, warmly appreciated and thoroughly considered *now*. By contrast, any whining and bitching over it at a later time will likely have at most some entertainment value – my entertainment that is. In fewer words: speak now or suffer later, your choice.

1. EuComms: Overall Goals:
1.1. All communications between clients and server to be encrypted.
1.2. Clients to be able to receive from server any data they lack (including maps, skins, sound or video content etcetera), on demand.
1.3. Clients to be able to choose and adjust both the level of security and their volume of communications with the server, as they will ultimately have to pay for the load that they generate.

2. EuComms relies on:
2.1. RSA with Keccak-based OAEP (via EuCrypt) for initial communication of OTPs (Serpent keys).
2.2. Serpent (via EuCrypt) for general communications.

3. Structure:
All communications between server and client will consist of packets up to 4096 bits in length, encrypted either via eucrypt.RSA or eucrypt.Serpent. The complete list of all such packets is included below.

Packets the client may send to the server:
3.1. Initial connection to server : client encrypts with the server’s public key and sends to the server a hello message; constructed as follows : following conversation in #trilema log, this hello message would actually end up multi-packet as it has to include the client’s public key (n,e so that’s currently 4096+2048 bits) instead of the id at 3.1.5 in the structure below:
3.1.1. Octet 0 – the length of the message in octets (43 or 76);
3.1.2. Octets 1 to 8 – the string “EuLoRa”;
3.1.3. Octets 9 to 10 – the major and minor versions of the communications protocol that the client wants to use;
3.1.4. Octets 11 to 42 – the client ID string, which will be equal to the eucrypt.keccak hash of the client binary. All client providers will be required to register all their ID strings with Minigame ; connections from clients with unknown ID strings will be rejected. Players will be notified of and helped to identify changes in their client software.
3.1.5. Octets 43 to 75 – the account ID string. If this field is omitted the server will create a new game account for the client.

3.2. Data (client sent)
3.2.1. Octet 0 – the length of the message in octets (multiple of 16 octets);
3.2.2. Octet 1 to 2 – descriptor of the type of data being sent (see annex A for datatypes);
3.2.3. Octet 3 to 5 – three octets ID of the item being sent;
3.2.4. Octet 6 to n – payload.

Packets the server may send to the client:
4.3. Data (server originated)
4.3.1. Octet 0 to 4 – the length of the message in octets (multiple of 16 octets, in principle limited to 65536 altogether and practically in the kbs anyway);
4.3.2. Octet 5 to 6 – descriptor of the type of data being sent (see annex A for datatypes);
4.3.3. Octet 7 to 10 – four octets ID of the item being sent;
4.3.4. Octet 11 to 16 – six octets counter of the chunk being sent (this limits downloadable items somewhat);
4.3.5. Octet 17 to n – payload.

Annex A — datatypes in client-server communication

NB: all data types below can in principle be sent by both client and server; however, data sent by the client is essentially a request to the server (i.e. it’s up to the server to decide whether it is valid and/or has any effect and/or what effect in game) while same data sent by the server is feedback/information (i.e. it reflects something that *has happened/exists already*).

In fewer words: client proposes, server disposes.

A.0 Basic types (used throughout the rest of sections in this Annex A)
A.0.0. char – character value on 1 octet
A.0.1. text – n*char where n will ALWAYS be specified clearly, right next to each and every text variable passed around
A.0.2. int8 – integer value on 1 octet
A.0.3. int16 – integer value on 2 octets
A.0.4. int32 – integer value on 4 octets
A.0.5. int64 – integer value on 8 octets
A.0.6. float – real value on 4 octets

A.1 Connection
A.1.1 Set of Serpent Keys

  • int8 – number of keys in this set, n;
  • n*(4*int64 + int32) octets – n Serpent keys (32 octets each + 4 octets ID for each, calculated as keccak hash of the 32 octets);
  • int8 – 1 for server use, 2 for client use, anything else undefined currently.

A.1.2 Request Serpent Keys –> response is a “Set of Serpent Keys” (A.1.1)

  • int8 – number of keys requested;
  • int8 – value 1 for server use, 2 for client use, anything else undefined currently.

A.1.3 Choose Serpent Key

  • int32 – ID of the key that will be used for communications from now on (depending on the key’s set, it’ll be used for server’s or client’s messages).

A.1.4 Burn & Choose Serpent Key

  • int32 – ID of the key to remove from usable sets;
  • int32 – ID of the key to use for future comms;

A.1.5 Unknown/Malformed Message received

  • int64 – id of message that was not understood.

A.2 File Transfer
A.2.1 File Information

  • int8 – length of file name in octets, n
  • n*char – file name (string)
  • int8 – file type
  • int64 – octets per unit
  • int64 – file size in unit given above
  • int32 – file hash (keccak)

A.2.2 File Content

  • int32 – file hash (keccak)
  • int64 – octets per unit
  • int64 – file size in unit given above
  • unit*file_size*int8 – file content

A.2.3 Request File Information –> response is a “File Information” (A.2.1)

  • int8 – length of file name in octets, n
  • n*char – file name (string)

A.2.4 Request File Content –> response is a “File Content” (A.2.2)

  • int8 – length of file name in octets, n
  • n*char – file name (string)

A.2.5 Request File Information –> response is a “File Information” (A.2.1)

  • int32 – file hash (keccak)

A.2.6 Request File Content –> response is a “File Content” (A.2.2)

  • int32 – file hash (keccak)

A.3 Chat
A.3.1 Chat Public

  • int64 – id of speaker
  • int16 – length of string being said
  • n*char – string being said

A.3.2 Chat Private

  • int64 – id of target for this chat
  • int64 – id of speaker
  • int16 – length of string being said
  • n*char – string being said

A.4 Character Actions & Activities
A.4.1 Basic action

  • int64 – id of subject
  • int64 – id of object
  • int32 – id of action attempted (client-sent)/performed (server-sent) –> see Annex B for list of current basic actions

A.4.2 Item movement

  • int64 – id of subject
  • int64 – id of object
  • int64 – quantity (stack count) to be moved
  • int64 – id of destination object (i.e. own char’s id for “pickup”, container ID for move to container, sector id for “drop” or move from container to world, other char’s id for “trade” or “store” etc.)

A.4.3 Item movement with specified slot

  • int64 – id of subject
  • int64 – id of object
  • int64 – quantity (stack count) to be moved
  • int64 – id of destination object (i.e. own char’s id for “pickup”, container ID for move to container, sector id for “drop” or move from container to “world”, other char’s id for “trade” or “store” etc.)
  • int64 – slot id

A.4.4 Activity –> this is meant to be sent by the server;

  • int64 – id of activity
  • int64 – duration (in seconds)
  • int64 – start time (unix format)
  • int8 – status: 0 -> finished/success; >0 -> ongoing / scheduled; <0 -> failed / aborted

A.4.5 Request Activity Information –> response is “Activity” above (A.4.4)

  • int64 – id of requested activity

A.4.6 Train

  • int64 – id of character
  • int64 – id of skill to train

A.5 Environment & Graphics
NB: “requests” here are not actions of the character in the game as such but rather supporting raw API for the client; for instance, requests here include obtaining the id of own character or available maps.

A.5.1 List of Sectors

  • int64 – number of sectors, n
  • n*int64 – list with ids of available sector

A.5.2 Request list of sectors –> response will be “List of Sectors” above (A.5.1)

  • int64 – character ID

A.5.3 Sector Map

  • int64 – id of sector
  • int8 – length of map filename (sector’s name)
  • n*char – map filename (sector’s name)

A.5.4 Request sector map –> response will be “Sector Map” type above (A.5.3)

  • int64 – id of sector

A.5.5 Item in sector (actor/character or object)

  • int64 – id of item
  • int64 – id of sector in which the item is located
  • float – position of item: x coordinate
  • float – position of item: y coordinate
  • float – position of item: z coordinate
  • int64 – id of graphics profile –> graphics profiles are so that the client can request and choose from alternative/equivalent actual files/meshes/icons/animations for this particular graphics profile
  • int8 – length of name/label of item
  • n*char – name/label of item

A.5.6 Request own char –> no payload here really since it’s accepted only from authenticated client so unambiguous anyway; response will be a “Item in sector” type above – it includes id of sector + id of char etc. (A.5.5)

A.5.7 Item in container

  • int64 – id of item
  • int64 – count
  • int64 – parent item/actor id (i.e. inventory is simply actor-as-container)
  • int64 – slot id
  • int64 – id of graphics profile –> same story as at A.5.5 above
  • int8 – length of name/label of item
  • n*char – name/label of item

A.5.8 Request item –> response will be “Item in sector” or “Item in container” types above (A.5.6, A.5.7)

  • int64 – id of item

A.5.9 Item Text Description (returned in response to a “view” basic action)

  • int64 – id of item
  • int64 – length of description
  • n*char – text description

A.5.10 Item Text Content –> sent in response to “read” basic action

  • int64 – id of item
  • int64 – length of text content
  • n*char – text content

A.5.11 Item List of Characteristics

  • int64 – id of item
  • int16 – number of characteristics
  • n*int64- the ids of relevant characteristics for this item

A.5.12 Item Characteristic

  • int64 – id of item
  • int64 – id of characteristic
  • int64(/float/int32/int16/char) – value of characteristic –> type here would depend on characteristic according to definition at A.5.13 below

A.5.13 Characteristic

  • int64 – id of characteristic
  • int8 – type of value of this characteristic (see A.0 above for the ids of data types that can appear here)
  • int64 – length of name of characteristic, n
  • n*char – name of characteristic
  • int64 – length of name of category, c
  • c*char – name of category

5.14 Request Characteristic –> response is “Characteristic” above (A.5.13)

  • int64 – id of requested characteristic

A.5.15 Skill

  • int64 – id of skill
  • int64 – length of skill name
  • n*char – skill name
  • int64 – length of skill’s category name
  • c*char – category name

A.5.16 Skill List

  • int 64 – number of skills in this list
  • n*int64- ids of skills in this list

A.5.17 Character’s Skill

  • int64 – id of character
  • int64 – id of skill
  • int64 – current rank
  • int64 – current knowledge points
  • int64 – current practice points
  • int64 – cost of knowledge to next rank
  • int64 – cost of practice to next rank

A.5.18 Request Skill Information –> response will be Skill above (A.5.15)

  • int64 – skill id

A.5.19 Request List of Skills –> response will be Skill List above (A.5.16)

  • int64 – id of character whose skills are requested

A.5.20 Request Character’s Skill –> response will be Character’s Skill above (A.5.17)

  • int64 – id of character whose current skill values are requested
  • int64 – id of skill requested

A.5.21 Time of day (game time)

  • int8 – hour
  • int8 – minute

A.5.22 Request time of day (game time) –> no payload, response is Time of day above (A.5.21)

A.5.23 Movement Type

  • int64 – id of this movement type
  • int64 – id of graphic profile for this movement type
  • float – x change
  • float – y change
  • float – z change
  • float – x rotation
  • float – y rotation
  • float – z rotation
  • int64 – length of name
  • n*char – name of this movement type

A.5.24 Request movement type –> response will be “Movement Type” above (A.5.23)

  • int64 – id of requested movement type

A.5.25 List of movement types

  • int64 – number of available movement types
  • n*int64- list of ids of available movement types

A.5.26 Request list of movements –> response will be a “List of movement types” above (A.5.25)

  • int64 – id of item for which available movement types are requested

A.5.27 Set Movement Type

  • int64 – id of item for which movement type is set
  • int64 – id of movement type to set

A.5.28 Destroy Item

  • int64 – id of item that is destroyed/vanishes

A.5.29 Set Item Position

  • int64 – id of item
  • int64 – id of sector in which the item is located
  • int8 – position of item: x coordinate
  • int8 – position of item: y coordinate
  • int8 – position of item: z coordinate

A.5.30 Neutral message –> mainly for feedback e.g. “X killed himself”, “That tool is not useful anymore”, “You ranked up.”, “You got loot”

  • int64 – length of text in characters, n
  • n*int8 – text

A.5.31 Error message (in game)

  • int64 – length of text in characters, n
  • n*int8 – text

A.5.32 Success message (in game)

  • int64 – length of text in characters, n
  • n*int8 – text

A.5.33 Message of the day

  • int64 – length of text in characters, n
  • n*int8 – text

A.5.34 Request message of the day –> no payload here; answer will be “Message of the day” above (A.5.33)

A.5.35 Top

  • int64 – duration in days from game’s “today” (i.e. 0 for today’s top, 30 for past months’ top etc.)
  • int64 – length of text in characters, n
  • n*int8 – text

A.5.36 Request Top –> answer will be “Top” above (A.5.35)

  • int64 – duration in days from game’s “today” (i.e. 0 for today’s top, 30 for past months’ top etc.)

A.5.37 Graphics Profile

  • int64 – id of this graphics profile
  • float – width
  • float – height
  • float – depth
  • int64 – number of contained slots with their own graphics profiles
  • int8 – length of filename for icon
  • m*int8 – icon filename
  • int8 – length of filename for mesh
  • n*int8 – mesh filename
  • int8 – length of filename for texture
  • p*int8 – texture filename
  • int8 – length of filename for animation
  • q*int8 – animation filename
  • int8 – length of filename for sound
  • r*int8 – sound filename

A.5.38 Request Top-Level Graphics Profile –> response is “Graphics Profile” above (A.5.37)

  • int64 – id of requested graphics profile

A.5.39 Request Child Graphics Profile –> response is “Graphics Profile” above (A.5.37)

  • int64 – length of path (i.e. how deep in the hierarchy is the graphics profile of interest)
  • n*int64- ids forming the path to the desired graphics profile (e.g. 2,1,15 means the 15th slot of the 1st slot of top graphics profile number 2).

Annex B – List of currently available basic actions

B.1 Target –> this is less than View at B.2. below; character targets the object.

B.2 View –> this is a generic view and can be applied to anything (objects, actors, world included).

B.3 Read

B.4 Lock/unlock

B.5 Use/Combine/Sacrifice

B.6 Explore (current /explore, with target sector id)

B.7 Repair

B.8 Exchange –> this would be used for at least 3 currently distinct parts: trade, store, bank. As everywhere else, its exact effect depends on context.

B.9 Complete –> for actions that require explicit “accept” confirmation from player (e.g. trade)

B.10 Abort –> aborts what the character is doing (if the abort is possible, permitted and meaningful in the given context).

B.11 Attack –> for current “/die” simply use this with subject and object the same, namely own character.

Use the comment form below for any questions, comments, suggestions or desires you have regarding this communications protocol.

May 3, 2017

Quest for the Crafty Noob

Filed under: Eulora — Diana Coman @ 5:19 pm

Eulora is this game where players get to actually do… everything. Even give quests to other players, when they want to and as (or if!) they like it. No more “bring me 7 rats’ tails and I’ll give you a rat’s ass” a la every other RPG out there but good old fashioned elder talking 1: here son, take ye olde blueprint and craft yourself a table first if you are serious about actually doing something in this life!

The table in question is not any table but the very useful and extremely versatile craft-table that Foxybot uses to carry stuff around when one doesn’t use it to craft or to store or to expand one’s inventory. And so the quest goes like this:

Find Foxy in game and ask her for the Crafty Noob’s first quest. Ask her nicely for she is under no obligation to give the quest to you! If she is however of a nice enough disposition to give you a helping hand, she’ll give you a few items, including a craft-table blueprint that reads like this:

craft-table_bp

She’ll also give you a bit of advice: take those items and go find the public crafting table that is close to one NPC called Electron Spirover, up a rocky hill. Once there, figure out how to use the things she gave you (as well as other bits that you might need but can find yourself with a bit of /explore) and make yourself your very own Craft-table!

As a bonus, you might even get some numina in the process. Or at least find out that you… could have gotten some numina such as Petrified Feelings. If only you knew how and why or when one gets such things…

  1. Do you even still find this in *real* life around you? Did you ever look for it?[]

April 13, 2017

Bundling with Foxybot

Filed under: Eulora — Diana Coman @ 3:04 pm

The ever useful Foxybot got shot a bit in one of its many legs at the last euloran update – previously fixed and reliable lists of ingredients turned all flighty and fickle, with ingredient numbers changing even as one crafts. As a result, automated craft runs can turn into a bit of a pain to both plan for (how many ingredients does one even NEED if each click potentially requires a different amount??) and to execute flawlessly and tirelessly over days and weeks, all foxybot-style. Still, people found a way around this trouble: just bundle stuff upfront and then …craft it, build it, eat it or whatever else you want to do to it, changing blueprint or not. And if bundling is the new planning and the new pre-requisite to successful crafting for a useful bot, let’s teach Foxybot to bundle, shall we?

You’ll need all of half an hour time and a bit of thinking – hopefully that’s something you *can* find around your house if not about your person at any given time. Grab your current version of the Foxybot and open its botactivity.h file. We’ll make bundling a new activity for the bot so simply plonk in there the declaration of this new activity, similar to the other ones you’ll see in the file. Since code is really the place for precision, not imaginary friends and flourishes, we’ll call it simply:

class BundleActivity: public BotActivity, iTimerEvent
{
};

The above lines declared the new BundleActiviy as a refined version of the generic BotActivity and of iTimerEvent. Basically so far it just brings those two together and it does nothing more than that. Clearly that’s not enough, so let’s add some useful methods and fields between those two curly brackets where the body of our new activity is to be defined:

public:
	BundleActivity();	//constructor
	~BundleActivity();	//destructor
	void HandleMessage(MsgEntry* message );	//this will react when server says something we care about!
	void ScheduleNewPerform(int delay);	//this is like an alarm clock - wake up lazy bones and do the next step
	bool Perform(iTimerEvent *ev);		//this IS the next step, called each time the alarm clock rings

	//for iBase			//those are the price of using crystal space; don't get me started.
	void IncRef(){}
	void DecRef(){}
	int GetRefCount() {return 0;}
	void* QueryInterface(scfInterfaceID i, int a) {}
	void AddRefOwner(void **, CS::Threading::Mutex*) {}
	void RemoveRefOwner(void**){}
	scfInterfaceMetadataList* GetInterfaceMetadata() {return NULL;}

protected:
	void StartWork();	//this is starting point, gotta start from somewhere, no?
	void WrapUp();		//at the end of each cycle one has to tidy up and this is just the code to do it
	void DoInit();		//at the start of each cycle one has to get all tools out and get everything ready

private:
  recipe rcp;		//this is the blueprint saying WHAT and HOW we are to bundle; can't do without it
  bool bundleReady;	//logic here is so simple that we can get away with just a few flags: is bundle ready?
  bool recipeReady;	//is recipe ready/read?
  bool bundlingStarted;	//has bundling even started?
  int containerID;	//id of container used for mixing; needed to filter view container messages for the correct one
  EID containerEID;	//this is Planeshit idea of ids: making at least 2 for each thing and several more to spare.
  int percent;	//used for choosing number of ingredients	- if only it would be percent...

Save botactivity.h and close it. Open botactivity.cpp and let’s now actually write all those things we just promised in the .h file. Ain’t no code working on empty promises and fancy headers, you know? So there we go, plonk in botactivity.cpp:

BundleActivity::BundleActivity()
{
  name = csString("bundle");		//we need this to react to /bot bundle
  textcolour = TEXT_COLOUR;		//what colour foxybot speaks in for this activity.
}

BundleActivity::~BundleActivity()
{//nothing to destruct really!
}

void BundleActivity::HandleMessage(MsgEntry* message )
{
	if (!IsOngoing())	//if I'm not working, then I don't care what server says!
	  return;
	//only if we are actually looking for bundle
	if (!bundleReady && message->GetType() == MSGTYPE_VIEW_CONTAINER)
	{//did things change in that container?
		psViewContainerDescription mesg(message, psengine->GetNetManager()->GetConnection()->GetAccessPointers());
		if (mesg.containerID == containerID)	//this is for the container we are working with, check if there is a bundle
		{//kind of lulzy if we were to look in the WRONG container, so check id, yeah

			if (mesg.contents.GetSize()>0 && mesg.contents[0].name.Find("Bundle") < mesg.contents[0].name.Length()) //it is a bundle { 
                                    bundleReady = true; //hey, we've got a bundle, we can go ahead! 
                        }
                }
        }
        else if (!recipeReady && message->GetType() == MSGTYPE_CRAFT_INFO)
	{	//this is when we need to read the recipe/blueprint that is equipped
		psMsgCraftingInfo incoming(message);
		csString itemDescription(incoming.craftInfo);

		rcp.ParseRecipe(itemDescription, percent>50);
		worldHandler::CloseOpenWindow(csString("Read Book"));
		OutputMsg(csString("Done reading recipe."));
		recipeReady = true;
	}
}

void BundleActivity::ScheduleNewPerform(int delay)
{
	csRef<iEventTimer> timer = worldHandler::GetStandardTimer();
	timer->AddTimerEvent(this, delay);	//set alarm clock
}

bool BundleActivity::Perform(iTimerEvent *ev)
{
	if (!IsOngoing() && !IsFinished())	//if I ain't working, I ain't performing.
		return false;

	if (timesDone >= timesToRepeat)		//done is done
	{	//nothing more to do here.
		Finish();
		return false;	//this means DON'T set up that alarm anymore!
	}

	if (!recipeReady)	//reading comprehension trouble? Just ask to read again. And again and ...
	{
		//ask for it again and wait another round
		psViewItemDescription out(CONTAINER_INVENTORY_EQUIPMENT, PSCHARACTER_SLOT_MIND);
		out.SendMessage();
		return true;	//re-schedule
	}
	else 	//recipeReady - we READ it, hooray
		if (!bundlingStarted)	//if it's not started, then...we start it now; simple!
		{

			OutputMsg(csString("Bundling action combining..."));
			char out[200];
			sprintf(out, "Done %d items, %d left to do.", timesDone, timesToRepeat-timesDone);
			OutputMsg(csString(out));

			if (!worldHandler::TargetEID(containerEID))
			{
				OutputMsg(csString("Could not find container within reach!"));
				Error();
				return false;
			}

			if (!worldHandler::OpenTarget())
			{
				OutputMsg(csString("Could not open the container!"));
				Error();
				return false;
			}
			OutputMsg(csString("Moving ingredients to container for bundling"));
			//move ingredients from inventory to container
			//first check for any bundles

			int toContainer = containerEID.Unbox();

			csHash<int, csString>::GlobalIterator iterIngredients(rcp.GetIngredientsList()->GetIterator());

			int nextEmptySlot = 0;
			if (!iterIngredients.HasNext())
			{
				OutputMsg(csString("Empty ingredients list!"));
				Error();
				return false;
			}
			while (iterIngredients.HasNext())
			{
				csString itemName;
				int quantity = iterIngredients.Next(itemName);

				char out[1000];
				sprintf(out, "Ingredient %s: %d", itemName.GetData(), quantity);
				OutputMsg(csString(out));

				psInventoryCache::CachedItemDescription* from = worldHandler::FindItemSlot(itemName, false);
				if (!from || from->stackCount < quantity) {
                                    OutputMsg(csString("Not enough ingredients for bundling! Bot stopping.")); 
                                    Error(); 
                                    return false; 
                               } else {
                                    worldHandler::MoveItems(from->containerID, from->slot, toContainer, nextEmptySlot, quantity);
				}
				nextEmptySlot = nextEmptySlot + 1;
			}
			OutputMsg(csString("Done with ingredients. Starting to combine."));

			worldHandler::CombineContentsInTarget();

			bundlingStarted = true;
			return true;	//re-schedule
		}
		else if (!bundleReady)	//we know bundling has started but..no bundle yet
		{
			if (csGetTicks() - startTime >= timeout)	//we have SOME patience; but no more than timeout
			{
				OutputMsg("Timedout, moving on.");
				//take items, ask for unstick + start over
				worldHandler::TakeAllFromTarget();
				worldHandler::ExecCmd(csString("/unstick"));	//this is so that ongoing actions are cancelled as otherwise everything is jammed afterwards
				DoInit();	//start all over again;
			}
			else
			{//maybe it IS done, but we...missed the memo; ask for it again
				//ask for container contents, maybe this got lost somehow
				worldHandler::OpenTargetEID(containerEID);
			}
			return true;	//simply reschedule here anyway, either way
		}
		else //all of them are true, so we're done, grab bundle + start again
		{
			//take items
			worldHandler::TakeAllFromTarget();
			Finish();
			if (timesDone < timesToRepeat) //if we are not done, go ahead
                        {
                          DoInit();
                          return true;
                        }
                        else return false; //done, yay
                }
}

void BundleActivity::StartWork()
{
  OutputMsg(csString("Bundle activity started."));
  ScheduleNewPerform(MIN_DELAY);
}

void BundleActivity::WrapUp()
{
  bundleReady = false; //at the end of a step, bundle is not ready!
  char msg[1000];
  sprintf(msg, "%d bundles done, %d potential bundles left.", timesDone, timesToRepeat-timesDone);
  OutputMsg(csString(msg));
}

void BundleActivity::DoInit()
{
  bundleReady = false;
  recipeReady = false;
  bundlingStarted = false;
  if (timesDone == 0)
  { //get parameter if any
    WordArray words(cmd,false);
    if (words.GetCount()>0)	//it has a percent, so let's get this; default it is 100%
	percent = words.GetInt(0);
    else percent = 100;	//default

   //get setup
   csString recipeName = worldHandler::GetBrainItemName();
   if (recipeName.CompareNoCase(""))
   {
      Error();
      OutputMsg(csString("No blueprint equipped, please equip the bp for what you want to bundle and /bot bundle again."));
      return;
    }
    else
	rcp.setRecipeName(recipeName);

  containerEID = worldHandler::GetTargetEID();
  if (containerEID == -1)
  {
	Error();
	//actionToDo = CRAFTING_NONE;
	OutputMsg(csString("Kindly target (open) the container you want used for this craft run and then /bot craft again."));
        return;
  }
  containerID = containerEID.Unbox();
}

  //read recipe = EVERY TIME!
  psViewItemDescription out(CONTAINER_INVENTORY_EQUIPMENT, PSCHARACTER_SLOT_MIND);
  out.SendMessage();
  OutputMsg(csString("Reading the recipe..."));
}

Now we only need to actually add this new activiy to Foxybot so that we can call it. Save botactivity.cpp and close it. Open foxybot.cpp and add the following line in the constructor below the other similar ones (it’s line 40 in my file):

activities[3] = new BundleActivity();	//adding the new activity to foxybot's "can do" list

Notice how Foxybot will know to delete it safely anyway since it deletes ALL the activities in its list in the destructor. But for that to work well we still need to let it know we increased the number of its activities. So save foxybot.cpp and then open foxybot.h and change:

static const int activitiesCount = 3;

…to:

static const int activitiesCount = 4;

That’s it. Save foxybot.h and close it. Then simply re-compile the whole thing:

jam -aq client

Get yourself a drink while the thing compiles and then launch the client again. Simply drop a table or something, equip the bp you want, target (open/examine) the table and type:

/bot bundle 2000

Bot should start work on making 2000 bundles of whatever you have equipped in your character’s mind. Drink that drink fast, for it won’t take long!

February 24, 2017

Basic toolchain for Blender – Cal3d – Crystal Space

Filed under: Eulora — Diana Coman @ 1:20 am

Only 2 days ago I was cobbling together a basic Cal3d+Crystal Space viewer of sorts for people to be able to test their Cal3d exported work to see if it’s of any use for Eulora. Well, 2 days around here turns out to be a very long time indeed, as meanwhile it seems that we got not one but even two Blender to cal3d exporters actually working as well as at least one new animated sprite that could well wiggle its way on Euloran soil. To help along any artists who think they could contribute but feel that they don’t have any sort of tools to support them, here’s a basic toolchain in its first iteration. You’ll need (DO mind the versions and don’t update/innovate on this topic):

  1. Blender 2.66 – you are best off downloading sources and compiling them on your own system. If you are not enough of an artist to do that, at least get your binaries from the official Blender site and CHECK their checksum.
  2. Exporter from Blender 2.66 to cal3d:
    1. I tested Julien Valentin’s exporter and it worked fine on a XUbuntu 12.04 64bits. So I’m hosting here the part you really need, check the sources page.
    2. To install it: simply drop the folder from that archive into the addons folder of your Blender 2.66 installation (you should find it if you go to your Blender folder and then further into 2.66/scripts/addons). Start Blender and go to User Preferences/Addons: either search for it or look in the Import/Export category. Activate the addon, save the preferences, enjoy. If all went fine, you’ll find it as an option from File->Export->Cal3d Model (.cfg)
    3. To use it: load your .blend file in Blender; go to Object mode; select both main Armature AND the mesh (you can do this in a view of the object if you use “a” for “select all” OR you can do it from the Outliner). Go to File->Export->Cal3d Model (.cfg) and choose where you want it to spit out everything. The exporter itself says that there should be a second step as it allows you to prune bones that are not needed – in my experience it rushes through it all in one go (admittedly my test model had only 4 bones so nothing to prune for sure), but here it is mentioned, just in case you experience this second step. Go and admire your multiple xml files with funny extensions (.xaf, .xrf, .xmf, .xsf)
    4. Alternatively, give a go to a different (if similar) exporter that was tested and found working on Debian 8.
  3. Crystal Space and Cal3d: download and install according to instructions on Eulorum. NB: do NOT use any other versions of Cal3d and/or Crystal Space or your work will probably be useless for Eulora if you do.
  4. Bash script xml2bincal3d.sh to help you bridge the gap between what the exporter spits out and what cal3d+cs actually need. Note that it can help you best when it has in the same place a header.cal3d and a footer.cal3d files that it uses to assemble a working .cal3d file for your use with the cally3d viewer (see 5. below).
    1. To use it simply download it, make it executable and run it: ./xml2bincal3d.sh inputFolder [outputFolder] [pathToCal3D] . By default, the output folder is same as input folder and the path to cal3D is taken from environment variable CAL3DHOME (so just set that one if you don’t want to use that last argument). Note that the path to cal3d IS NEEDED in order to actually run the converter between xml and binary cal3d formats. While you CAN run a similar converter that comes with your distribution, it will most likely NOT match the cal3d version that the exporter uses and it will miserably die on you.
    2. The script goes through all the .xsf, .xrf, .xmf and .xaf files in the inputFolder, calls the cal3d converter on them and stores the results in outputFolder/modelfiles. It also creates a file outputFolder/test.cal3d that includes all the exported skeletons, meshes and animations. NB: while this IS a valid file in general, you might want to check it and/or adjust values in there to match your needs!
  5. Viewer cally3d_args based on Cal3d+Crystal Space so that you can check your work. For sha512sum and more detailed instructions see the sources page.
    1. To use this, you’ll need to compile it. A simple “make all” will do, provided you have already cal3d and Crystal Space installed.
    2. Once compiled, run as ./cally3d [file.cal3d] [animationName] The default values here are “test.cal3d” and “strut” respectively. To use your previously exported model you can:
      1. run the script at 4 above with the folder of this viewer as outputFolder OR
      2. manually copy the output from 4 to this folder
      3. give as argument to cally3d the full path to your .cal3d file. NB: in this case you WILL likely have to update manually the .cal3d file as well so that cally3d can find the skeleton/animation files.

When you finally run cally3d, you should be looking at your model performing whatever animation you asked it to perform. Note that the bash script gives animations their name based on the name of the .xaf file, so you might really want to follow the convention and name your animations in Blender as modelname_animationname rather than myAnimation sort of thing. If your animation is for instance wormOfDoom_knotting, then you’ll be able to ask cally3d to show you the “knotting” animation of your worm of doom. Otherwise you can of course ask for anything you wish and get a load of errors.

As previously mentioned, this is a very basic toolchain cobbled together more as a prototype / starting point rather than a definitive reference. The most glaring current limitation (by no means the only one) is the lack of material support – whatever your worm of doom was wearing as its skin will be quite bluntly ignored by the viewer (though not by the exporter).

Enjoy! And if you complain, do it with concrete and precise descriptions of what hurts, where and what you did when it started. Unless you want it to hurt even more, that is.

January 23, 2017

Ordinary trouble of an Euloran explorer

Filed under: Eulora — Diana Coman @ 10:57 am

(This could certainly be a whole category of troubles Foxy’s bloody diary, if only there were categories to all the trouble.)
As a seasoned explorer of Eulora, maker of maps and of bots and of whatnots, Foxy knows where to search to find troublePetrified Bubbles, Dead Molluscs or other things of that sort. In a word, things that are worth 100+ times more than the basic stuff that can be found almost anywhere. For the naive explorer of words-other-than-Eulora, this might seem as the very opposite of trouble – the light, the profit, the happiness, the printing of money – in a word heaven without hell and earnings without losses or something of the sort. However, Eulora just doesn’t work like that – it simply doesn’t do all this “there shall be no trouble” thing and most probably it will never do it, not even when pigs 1 fly all the way to the moon.

To illustrate this interesting 2 lack of unbalanced happiness on Euloran soil let’s consider for a second that you’ve got – as Foxy did – not only the rare knowledge of where Petrified Bubbles or Microgoats are, but also the concrete keys to corresponding claims 3 for such resources. Such claims come in various sizes and increasing cost of building 4:

Tiny claims take 1 Little Bit O’ Nothing to build and so they cost 11 coppers.

Small Claims take anything between 1 and 7 Coarse Frangible Threads and so they cost between 180 and 1260 coppers.

Ordinary Claims take all sorts really, but adding them at base value reveals that they simply take about 15000 coppers, give or take some change. More precisely:

Ordinary claim of Petrified Bubble is 6*262+11*67+4*59+5*399+5*1963=14355.

Ordinary claim of Microgoat is 2*5439+2*1096+11*79+1484+73=15496

On the bright side, those two ordinary claims don’t even require anything really rare – the PB claim takes some molluscs, but that’s not a terrible issue. And the microgoat one takes one pacademia nut and otherwise plain and simple oil, almost as boring as it gets 5. So then: what’s the trouble?

The trouble is of course on the less-than-bright side, namely output and value and the likes. Out of the tiniest of claims (and thus for as little as 11 coppers), one will get at least one item. So one PB perhaps, meaning a whopping 43000 coppers and change. Or one microgoat with its rather macrovalue of about 86000 coppers. And that is only base value, as one can easily (sort of) get higher quality and therefore multiply those values by a factor of 2 or more. Small claims are especially good for this seeing how they easily allow overbuilding – the use of 7 threads instead of 1, thus pumping up the cost of building the claim but also the quality (hence, value) of output. For most resources *other* than those 2 discussed here, ordinary claims also quite imply overbuilding, since the 15000 coppers going in are certainly more than the 73 cost of one crumbly rock or even the more considerable 1963 cost of mollusc. However, for PB and Microgoats, ordinary claims are suddenly quite a pain to truly overbuild – at 86000 the microgoat and 15500 the ordinary bundle one needs 5.54 times the base value of the bundle (hence 554 quality points) to only match the smallest output at base value.

Sure, the above means that one is at least quite guaranteed a profit out of building an existing tiny/small/ordinary claim of PB or Microgoat. Assuming of course that there was no accumulated loss in *getting* such claim – quite a naive assumption. Nevertheless, the weirdness and trouble starts when one considers what quality of bundle should one make to build an ordinary PB or Microgoat. Should it be the lowest quality possible? That would mean apparently high profit as one PB is one PB and one Microgoat is really no smaller than another. However, a bundle of quality 1 will ALSO lower the quality (and therefore the value) of the output. Similarly, a high quality bundle will increase the quality of the output and given that each quality point of a PB is in fact worth 431 coppers and each quality point of Microgoat is worth 859 coppers, that’s arguably not such a small matter. Adding to that the markup incurred on different ingredients required for the ordinary bundle, the whole thing suddenly becomes rather difficult to clearly decide on – and then one wonders simply why the ever loving fuck one got an ordinary instead of a plain old uncomplicated tiny or small – the result would STILL be 1 PB or 1 Microgoat, overbuilding the hell out of it would be waaaay simpler and more straightforward, underbuilding it would give no qualms and the trouble overall would be quite non-existent.

So then, can anyone come with any idea as to the actual usefulness of ordinary claims 6 for high value items such as PB and Microgoat? Other than providing trouble, I mean. And perhaps the cuteness of an Ordinary Microgoat:

shot53

  1. They are called Multifunctional Samovars in Eulora for some reason[]
  2. Some might call it painful, by another name.[]
  3. Basically they would be the equivalent of veins I suppose – they mark the place where you need to build something to extract the actual resource – in unknown quantities, mostly small quantities, sometimes large and at rare times huge.[]
  4. Will use base value throughout, to keep things simple at least for starters.[]
  5. Boring IS good on Eulora – you don’t want the sort of excitement that happens there[]
  6. It’s unclear – due to lack of data on the matter – whether the next size up for claims, namely Sizable would even do, but one can always hope it does and then go and dig.[]

December 5, 2016

The Sentiments of Grass

Filed under: Eulora — Diana Coman @ 11:50 pm

(This could be – but nobody knows for sure anymore – another page out of that half-eaten Diary that Foxy used to keep. It all starts with pains and names and all that.)

Since its early days of broken Maths and painful cooking, Eulora has certainly grown up in leaps and bounds – or rather limps and hobbles. It grew up in fact so much that one might say it started quite developing – for starters it developed its own feelings and sentiments, dusts and mites, lint and horrors and all sorts that it calls with one word – numina.

Feelings 1 in Eulora are quite abundant and totally worthless. At least the low quality, 100 a dime feelings that anyone can easily get – for Eulora’s advances most pointedly include some rather ruthless Mathematics that apply to all that shiny new and totally petrified sentimental numina. Feelings of superior quality – superior in Euloran sense, aka “slightly less horrible than the usual” – though have recently been shown to be – of course – quite difficult to get. How difficult? Well, difficult enough that you’d spend about 200% their base-value 2 worth to even get some, it would seem. Difficult enough that you’d first use all your 10 thoes to dig up some shiny, slinky, silky grass that WAS actually happy to be alive, then burn up the increasingly-rarer-and-hard-to-get coarse frangible thread blueprints, spend your days and nights hunched over a craft-table threading grass and then – only then – you’d hope to get some such lofty – despite being petrified really – feelings. As well as a hefty loss in terms of base value, of course – remember that ruthless Mathematics? It counts feelings with a big fat minus in front!

Perhaps feelings being so worthless in Eulora is hardly surprising given that people found before and in other parts that feelings are downright evil and out to get you when you aren’t looking – at least when you aren’t looking in all the right places, at the right thing and in your right mind that is. But even after discarding therefore those feelings in the dust where they can do least damage (to your finances at least), there surely are – there MUST be – the more valuable sort of numina, the Sentiments 3. Oh, they certainly are more valuable – about 100 times more valuable. And almost impossible to get out of all that shiny, luscious grass that you’ve been… threading last night, full of feelings and what not.

Almost impossible, but… not quite. Rare indeed or rather worse than rare – meaning at a huge loss – sentiments it would seem can be had even of the grass variety. Welcome therefore to Eulora, this most sentimental island – tread with care for the grass itself is full of…feelings! 4

 

  1. Tinkerer’s Petrified Feelings, to be precise.[]
  2. At least 200%![]
  3. By their full and very imaginative name – Tinkerer’s Petrified Sentiments []
  4. And I don’t know about your own feelings and all that, but if they are the usual Euloran type, they surely ARE out to get you, yeah.[]

September 18, 2016

Happy 1st Anniversary, Foxybot!

Filed under: Eulora — Diana Coman @ 2:54 pm

While patting my reportedly very well-behaved bot on its neat and tidy state machines, I stumbled upon the proof of its inception in all its memorable words, one year and a few days ago:

diana_coman: and yeah, I am making progress on a bot to do it since yest; since I will never do that again by hand for sure

That promised progress was indeed made, that click-frustration was indeed channeled into code and one year later Foxybot tirelessly works in Eulora for everyone and anyone who can actually read. And for those who can’t, it just waits there in the background with the endless patience that might simply be indifference all the same. It waits nevertheless one single command line away, always ready to help, never taking over. As far as I can see, its wait is long with many noobs, its usefulness is fully appreciated by the veterans 1, its benefits are just as well reversed in the wrong hands. One player reportedly fucked up all his rather expensive exploration tools (chetty sticks) by setting up the bot with the wrong timeout – and not even checking on it apparently. In the player’s own words: “Yep, i fucked up pretty gud“. All in all, a happy bot existence if there ever was one.

Born out of frustration on the murky shores of euloran client code one year ago, Foxybot grew from a simple tinkerer of sorts to master of exploration too 2. Some people suggested it might even be a deity of the land, worthy of a cult of its own. It cooked countless items in people’s samovars and it tinkered all sorts on their craft-tables, in their bandar toolkits,  lapidary tables and what-nots. It made maps for Foxy, it found the famed Dead Molluscs, the infamous Slithy Tove and the rare Petrified Bubble. It brought exploration to a whole new level when it started using the craft-table to avoid mixing qualities and to work around the weight limitations of its masters. And close to its first anniversary, it made it into client release full and proper, a most useful and most used part of the game itself.

Happy first anniversary, Foxybot! May you always remain true to your best description so far:

mircea_popescu: people used to do this for a long time by hand lol

May you grow and learn forever more!

And as a present from Foxybot to all its euloran users, here’s a fragment of an old, old map on which the secret hiding place of molluscs was first revealed:

tomb_of_molluscs

Can you find out the coordinates of the purple spots that are the molluscs? There’s a patch of shed snakeskins (top left) that might help you locate this on the larger map. Good luck!

 

  1. There are many examples scattered among the logs, such as this: “it’s a huge boon for miners“. And some reports of quite effective use by previously rather reluctant adopters. []
  2. In other people’s words: “foxybot with pickaxes is fucking epic[]

August 4, 2016

Craft-Table Tourism with Foxybot 1.4

Filed under: Eulora — Diana Coman @ 11:31 pm

If you haven’t yet seen Foxy dragging around a craft-table as part of her new drop/explore/build/take/move/drop ritual, it’s only because you haven’t been looking at Eulora lately 1. So better go and grab yourself the latest client 2, start compiling it and while it compiles, come back and read the story of the little bot that could do wonders with a humble craft-table. And yes, Foxybot 1.4 is now part and parcel of Eulora’s client 0.1.2, hooray!

shot42

When Foxybot was as little as 1.3, it could happily explore for quite a while, but it horribly mixed items of different qualities and it inevitably got stuck when all it got was too heavy to even move any further. To avoid this, the whole explore got a few more steps of dancing with the craft-table. All you have to do is to make sure that the first table in your inventory is empty(first table, but not necessarily in the first slot – so you can have an empty table in the 10th slot, as long as in slots 1-9 you have anything *other* than a craft-table or just nothing) and then start the bot, for instance like this:

/bot explore 10000 line 10 1 7 7500 8500

The above means that the bot will do 10000 explore attempts, going in a line that is 10 attempts long, leaving the key in the claim after building (flip that 1 to 0 if you prefer to keep the keys), checking every 7.5 seconds to see how things are going and giving up on waiting (timeout) after 15 seconds (technically after 8.5, but because of 7.5 seconds between checks, it will be 15 seconds in total until timeout). More precisely, the bot will do the following:

  1. Get first table in inventory and drop it.
  2. Explore
  3. Build claim (if any)
  4. Take resources
  5. Put key in claim (if flag is 1)
  6. Move resources to the first slot in the table that is on the ground and *assign* this slot to that resource AND that type of claim (for instance: flotsam_tiny or flotsam_small) 3.
  7. Move one step
  8. Pickup table
  9. Drop the table that was just picked up (hence NOT whatever other table might happen to be first now) and *repeat* from step 2.

The sequence above means that resources from tiny and small claims of the same type are kept separate. It also means that you really can explore for as long as you wish without becoming overweight, since everything is in the table and the table is just dragged on one step at a time (the movement is done when table is on the ground so there is no overweight issue). However, there are a few caveats, namely:

  • CaveAtAbundance: Like it or not, the craft-table has “only” 16 slots and each resource will take in the end two slots (1 for small and 1 for tiny). Hence, poor bot can fit only 8 different resources before it runs out of space in the table. Being a stubborn bot as it befits a machine, it will keep going nevertheless by keeping separated what it can (what is already in the table) and otherwise mixing (despite its best efforts) everything else. Hey, it’s your bot, not your master to know what is what!
  • CaveAtPoverty: Bot works hard and finds claims, but claims are hungry for lbn and cft. Feed your bot appropriately with enough cft and lbn to last it or you’ll come back to an inventory full of keys to tiny and small claims (mostly). You’ve been warned – have fun and ignore it, bot won’t care either way.
  • CaveAtMeddling: The bot knows what it knows and nothing more – it’s a state-machine! More to the point, if you start moving things around in its table, it won’t know and it won’t care – you are the master, you are free to mess things up – the bot will just keep doing precisely what it was doing. So if you for instance move some grass to its previously flotsam-tiny slot, then it will still try to move its next stack of flotsam into that slot. The server being quite uppity about it, will simply stack the flotsam instead into the next free slot in the table. In turn, this will cause further grief when the bot tries to put in *that* “free” slot something else and that gets moved further by the uppity server and so on and so forth. Hey, you wanted meddling? There’s no better chance than this to turn it all into disgusting goop. Bon appetit!
  • CaveAtRestarting: If you stop your bot for any reason, be aware that you are probably better off by first stashing somewhere all resources found so far before starting the bot again. Otherwise the resources will probably mix horribly (server mixes what you take with anything in your inventory, including something that is *inside* a table). So either go with them to storage or dump them in a claim and lock it, but really, get rid of them, reset the bot (/bot reset) and only then start it again.

Some of the caveats above also have workarounds if you know what you are doing 4. For instance, if you really have some stuff you want in that first table (maybe because it is too heavy and you can’t move with it), you *can* still make it work fine: just put that stuff in the *last* slots in the table, since the bot will nicely start with the first slots. And make sure that you don’t end up in the CaveAtAbundance dark corner, since there will be therefore space for even fewer than 8 resources.

Other than the above and as always, Foxybot is not only easy to change, but also easy to replace: simply replace any of its files or its whole folder with the corresponding files/folder of a previous (or your own) Foxybot version, as you wish. Recompile the client and you are all set, there is no incompatibility as such.

First users have already reported quite inventive uses in getting the bot to explore all the way to a mining spot. There certainly are others, so go bend the bot in new ways all of your own. Enjoy!

  1. And if you haven’t looked at it until now, you already missed more than meets the eyes, but that’s your loss, not mine.[]
  2. Version 0.1.2b, as older versions won’t work with the server freshly deployed this Wednesday.[]
  3. For the curious but too lazy to read the code: this is implemented with a hashmap that has as key a string created from the type of the claim (tiny or small) and the resource’s name and as value the number of the slot assigned in the table. This is because currently the client is dumber than the dumbest terminal and therefore there is no reasonable way for the bot to actually know what is in the table (short of asking the server *every* time). So the bot is basically blind to the world as it is, having only its own limited image of the world as it *should be* according to its previous actions. Feel free to discuss the Foxybot philosophy but keep in mind it’s not even of school-age yet.[]
  4. Better know – or find out – what you are doing at least half as well as the bot does.[]

March 1, 2016

Coordinate Eulometry (or Tomb of the Dead Mollusc)

Filed under: Eulora — Diana Coman @ 12:15 am

In the green and unforgiving world of Eulora, I am an explorer and mapmaker through necessity more than anything else (is there anything else?). As a result, I have made and even published maps and location of resources, but lo and behold, people want more! More of what? Well, more information and more useful things made by others, of course, what else. Excellent, you might say, they want it, you have it, sell it to them and everyone’s happy, industrious and productive like never before. Perfect situation, perfect plans, perfect idea, except…do they want to pay what it takes? Not quite, no.

Given the above, here’s where I become an explorer of potential deals and potential ways of pricing immaterial things in Eulora (euloran knowledge no less!) – I become one through necessity of course, more than anything else 1. As if pricing of basic items was solved already, here I am, diving straight into attaching some price to knowledge, what can go wrong.

Considerations aside and going straight for the throat of the matter, here’s how things stand: the current most-prized knowledge of resources is the location of the fabulous Dead Mollusc. Location which I’m the only one to know. This knowledge of mine is the result of painful and meticulous island-combing by Foxy over several months. Island-combing which was made possible in the first place only as a result of quite another piece of significant work – designing and implementing the bot itself. So then, do you think that all this would be worth 10 bitcents at least? Well, so far it doesn’t seem to be worth much to eulorans at all.

Apparently, this unique, useful and really quite difficult (as in: expensive but also time-consuming and not trivial) piece of information is worth at most 3 bitcents or, perhaps, some parts of some mollusc cheese at the further cost of not-yet-found slithy toves. What to do then? Take the 3mn? Take the cheese and give some more unique and not yet found resources? Keep the secret of the molluscs’ tomb? Since I don’t find any of these options particularly good, here’s a fourth: making concrete and potentially useful map knowledge available instead. Smaller pieces of information, smaller (arguably more digestible) prices, unknown, but potentially higher return. Judging by the long successful history of euloran official auctions of previously-unknown items, players quite like paying for unknown but potentially high return.

Here’s what I have:

1. 100`217 distinct points 2 at which I *never* found anything, despite trying between 1 (inclusive) and 10 (exclusive) times.
2. 1139 distinct points with 0 successful attempts and between 10 (inclusive) and 20 (exclusive) failed attempts.
3. 252 distinct points, 0 successful attempts, between 20 (inclusive) and 30 (exclusive) failed attempts.
4. 111 distinct points, 0 successful attempts, between 30 (inclusive) and 40 (exclusive) failed attempts.
5. 119 distinct points, 0 successful attempts, between 40 (inclusive) and 50 (exclusive) failed attempts.
6. 24 distinct points, 0 successful attempts, between 50 (inclusive) and 60 (exclusive) failed attempts.
7. 23 distinct points, 0 successful attempts, between 60 (inclusive) and 70 (exclusive) failed attempts.
8. 24 distinct points, 0 successful attempts, between 70 (inclusive) and 80 (exclusive) failed attempts.
9. 22 distinct points, 0 successful attempts, between 80 (inclusive) and 90 (exclusive) failed attempts.
10. 9 distinct points, 0 successful attempts, between 90 (inclusive) and 100 (exclusive) failed attempts.
11. 19 distinct points, 0 successful attempts, between 100 (inclusive) and 200 (exclusive) failed attempts.
12. 1 point, 0 successful attempts, 217 failed attempts.
13. 1 point, 0 successful attempts, 1320 failed attempts.
14. 1 point, 0 successful attempts, 2987 failed attempts.

Based on my exploration experience so far, I am fairly sure that some of the resources that haven’t yet been found are precisely at the last 3 points above if not at some others in those packages there. However, based on my same experience so far, I am also quite sure that one needs either high enough rank or expensive enough tool in order to actually get those resources there where I missed repeatedly. So be warned that I have found before *different* resources in the same spot, which means that I wouldn’t really trust 1 single successful attempt to fully tell me ALL that there is that one spot.

In any case, I’m really quite curious what eulorans think of the above 14 packages. Note that if I start from 1 copper per point at package 1, 10 per point at package 2 and so on, that still dwarves very quickly the 10mn per found (no surprises there) Dead Mollusc location.

As they say around here: looking forward to hearing from you!

  1. Honestly: is there ANYTHING else on Eulora?[]
  2. Each point is identified through its x and z coordinates, considered as rounded integers. For example, point (15, 15) covers the small square from (14.6, 14.6) to (15.4, 15.4). If you are concerned about such granularity, do note that even the tiniest touch of a movement key that results in a barely perceptible change of position in game still bumps the coordinates by 0.1.[]

January 28, 2016

Foxy’s Musings on Click-Slaves vs Apprentices and Their Choices

Filed under: Eulora — Diana Coman @ 9:27 pm

As a noob on Eulora, you don’t have a clue – but you still have a choice. The choice being of course between losing – both your money and your chance at cracking up the only game that could actually pay you a living at some point 1 – and winning. Winning or more precisely actually earning – truly earning your money (as much of it as you can really), earning your place and earning your voice in the future. Making the first choice – the choice of losing – is quick and simple: just dive straight in, without reading anything and without thinking through anything (and by thinking I mean doing some very concrete and quite clear calculations, too). The choice of winning might seem too vague, too complicated or even too difficult to make through the fog of noobishness, but know that if you let that stop you from even trying, than you really just made your choice already – your choice to simply lose.

At a first glance, between those two amazing 2 options, there is enough space to wedge in at least a third – the playing it “safe” option, putting your whole trust 3 and more importantly earnings in the hands of “elders.” Provided you know at least how to choose the right people to put your trust into, that can even be all right for a start, for a jump-start especially, for a while. The trouble is that it is really only for a while, really only if you use it as an opportunity to learn faster than you could on your own. In other words, if you take its safety aspect as only a side effect rather than the main point: the point of placing your trust in others – carefully chosen others – is that it can help you learn faster, NOT that it protects your ass.

Update (28 November 2016): note that the numbers below are NOT accurate anymore because Eulora has changed A LOT. The principles above remain however perfectly true – you just need however to work out for yourself concrete examples that are more up to date than the one below:

Now that we put the ass behind rather than in front of us, let’s focus a bit on that painful part of figuring things out. Currently, most noobs landing in Eulora get to do jobs kindly offered by others. They are basically provided with everything they need (tools, ingredients, skills and crafting accessories even) and a 10% share of the result as form of payment for their work. For gathering work 4, the elder provides tools or claims as well as bundles, the noob does the clicks and hands back over the results. For crafting work, the elder provides tools, blueprints and ingredients, the noob does the clicks and hands back over the results. The noob doesn’t pay anything – hey, no risking your own money, eh? – and he doesn’t own anything, although he gets in the end a small – and quite fair – pay for his clicks. There is usually no ranking up of skills for noobs, least they somehow get out of their noob status and “lose” the perks of being click-slaves to others. All this is, of course, through the noob’s own choice as nobody can ever stop you from ranking up in Eulora. Still, there it is, no ranking and hardly any learning, as that’s considered “safest.” And since the safety of your ass comes first before any longer term considerations, what is there more to say? Go click merrily ever after. And don’t read further, as it might still bite you 5.

Since I don’t quite agree with the above model, I always offer it only as a last-resort alternative. I’d much rather work with noobs that want to become apprentices and therefore learn and grow until one day they can also be elders and perhaps help others grow too 6. For this reason and for the benefit of my current apprentice, I recently ended up running for him some basic numbers and outlining a possible longer-term strategy. Since this might actually help others too and since as far as I know there isn’t at the moment any other kind of strategy discussed for Eulora anywhere in one place and in clear 7, I decided it would be good to have it written here too.

To start off: there are two main ways of making a profit in Eulora at the moment. One is basically gambling through “overcrafting”: you use high quality inputs to get low quality outputs (being a noob basically means that you get low quality output due to lack of skills/levels) and make your money from either a). Quantity b). Loot. This currently works great for resources, especially if you build the claims of a highly skilled gatherer and with high quality ingredients. One click and you get thousands of resources – low quality ones, but which sell anyway at a price corresponding to middle quality as it were. It also works well for crafting although in a slightly different way – you get blueprints as “loot” and those blueprints can fetch you a tidy sum. The other way of making a profit in Eulora is more reliable in some sense – essentially you make a profit by adding value through your skill: you use low quality inputs to produce higher quality outputs. In this case there usually is no loot and no huge quantities, but there is a predictable and guaranteed value added as the difference between the quality of your inputs and that of your output.

To illustrate all the above, here’s the calculation I did earlier for one of my apprentices who currently builds at quality 7:

– Making slag = 5 flotsam + 3 shiny rock, approximately 20 seconds with the FoxyBot (fully automated).

– Base value 8 of flotsam: 88

– Base value of shiny rock: 79

– Input value based on my apprentice’s own resource gathering (say, his 10% share of some building work he did for me): 0.07*(5*88 + 3*79) = 47 coppers

– Current market value of the input: (3*79+5*88)*(0.07+(1.54-0.07)/2) = 545 coppers

– Current market value of the output at quality 10: 677*(0.1 + (1.54-0.1)/2) = 555 coppers -> added value is 10 coppers per slag, hence 30 coppers per minute.

– Current market value of the output at quality 20: 677*(0.2 + (1.54-0.2)/2) = 588 coppers -> added value is 43 coppers per slag, hence 129 coppers per minute.

– Current market value of the output at quality 30: 677*(0.3 + (1.54-0.3)/2)= 622 coppers -> added value is 77 coppers per slag, hence 231 coppers per minute.

Consider now that you can run FoxyBot over the night. In 8 hours straight, it will make 8*60*60/20 = 1440 slags, eating up 1440*5 = 7200 flotsams and 4320 shiny rocks and landing you a net profit on tinkering alone of 1440*77 = 110`880 or a total of 1440*622 = 895`680 coppers (satoshi) made literally while you sleep (assuming tinkering at 30q). And that’s certain, predictable money right there, without relying on luck or anything else. You can even take and fill orders in fact!

Based on calculation such as those above, my suggestion of a possible strategy for an apprentice is quite simple: team up with a highly skilled gatherer and get your 10% in resources rather than coppers. Then train your tinkering skill (either using some books or – cheaper – just training it when you do some overcraft on tinkering). Then simply start making and selling all sorts of crafted items that are highly needed and used as further input for more complex crafts (such as slag above, but also disgusting goop and others). Making more than 1 bitcent each day (24 hours) while also growing your skill and getting to experience Eulora from a different position than that of a click-slave.

Note also the beauty of it all: it really is a place best fit for a noob *and* it is a scheme that you really can even run quite fully by yourself. You can buy chetty sticks that will give you 9 only ordinary claims (hence relatively high output for your low quality) on basic resources. So you don’t even depend on the highly skilled gatherer if you really don’t want to 10. You also don’t depend on anyone for blueprints as you can easily get some more at any time: just buy a few high quality resources and do some overcrafting increasing your quality output AND getting blueprints in the process. As long as you keep your building low and increase only your tinkering, your building feeds into your tinkering wonderfully.

As to best fit for a noob: consider a highly skilled crafter. They do indeed extract significantly more value from the same input resources. However, do they really want to spend their time undercrafting and moreover, undercrafting slag of all things? For one thing, while they extract value through undercrafting, they are effectively stagnating in terms of skill while doing that. For the other, if they choose the undercrafting strategy as a way of making money, they would actually earn more by doing items that are way more complex than those 11 – using in the process your output in fact. The difference in quality between input and output is effectively a percentage difference in value added, hence the higher the value of the item one makes, the higher the amount of money they earn at the same level of undercrafting. So if you see now highly skilled tinkerers undercrafting slag and the like it’s really because there’s nobody else to do it – in all likelihood, they’ll be quite happy if someone finally steps in and do the job, so that they can do all the more complex (and more expensive) crafting that is needed.

As a final note: you can also just keep playing it “safe,” leaving to someone else all the calculations, planning and pain of actually doing something other than clicking. Of course you can and you’ll still earn your share, no problem with that. Just remember though, that so can all the other noobs. And noobs there will always be in Eulora like anywhere else. How safe is it exactly to remain forever at the very start, perfectly fungible and indistinguishable from the next noob and the next 1000 noobs with whom you will inevitably start competing ever closer, ever fiercer, ever more dangerously. How is that exactly the safe strategy, can you tell me?

  1. A more than double loss if you come to think about it, but hey, your choice is the best choice, right?[]
  2. And passionate, did I say passionate enough?[]
  3. Did you actually even consider that, hmm? Like really consider it, with all its implications – putting ALL your trust into others and into their understanding of a world you just stepped in, naked and penniless and more vulnerable than you can even begin to understand. Did you really consider what you really trust those people with, when you decide to just do their bidding without even trying to figure out things for yourself?[]
  4. Gathering resources, so exploring and/or building.[]
  5. In the ass, where else?[]
  6. Sure, I CAN and WILL work with those who are happy to remain click-slaves for ever, but I’ll also consider them precisely as such and nothing more than basically fungible click-providers. []
  7. There are bits and pieces scattered throughout the irc logs I’d say, but that’s about it.[]
  8. Value of an item/resource at quality 100. An important concept in Eulora, this one, so better get your head around it sooner rather than later. You can find base values of many items and of all resources known so far on their respective page in my cookbook.[]
  9. due to your low gathering skill[]
  10. It might be more lucrative though over a longer period of time as they’ll get higher yield claims on average even if not at every hit.[]
  11. You’re warmly invited to run the numbers and see for yourself.[]

January 21, 2016

Making Patterns on Euloran Soil

Filed under: Eulora — Diana Coman @ 12:03 am

(This is some day like all the others in Foxy Foxster’s diary on an island with more trees than people.)

One day, Foxy Foxster 1 got bored of scribbling in her diary and of sifting through loads and loads of data collected by her ever perky Foxybot. So she plotted the data instead, basically creating cave-art. Or mud-art. Anyway, ART, ok? Such as:
grass_spot

The circle is made of course just to mock future generations that are bound to discuss at length its deeper meaning and carefully designed role in some forgotten ritual. If there were more boulders around looking like boulders, one might even recreate that in stone at the scene of the mocking. And call it – very imaginatively – Boulderhenge.

Boulders, circle and art aside, the grid is really the more important part there. See how neatly aligned those points are above the circle? That’s Foxybot’s super-precision mining, digging up something at each and every 3 coordinates. Over and over again Foxybot went and burnt pickaxes, hoes and sticks in the ongoing attempt at finding things that everybody saw but nobody found as yet: slithy toves, rotten giant’s canines, petrified bubbles and the like. To give you a bit of an idea of all that digging, here’s how it looks like, on a larger (and messed up for presentation purposes) scale:

attempts_eulora_island_20jan2016_1

The key in all the grid above being of course…not the grid itself. But the white spaces – those annoyingly many white spaces in there. That’s how much one can still miss even *searching* in, even looking at, unless really measuring, counting and plotting. Now only if the plot really was something more than a bunch of numbers on a cave wall…

  1. She’s a female on an island, without any decent cooking skills or stoves, without shoes and with only the books she actually writes herself. Not to mention with only very, very few actual males around. Not sure if the island is just slightly off some continent too.[]

December 28, 2015

Euloran Cookbook V1.2

Filed under: Eulora — Diana Coman @ 4:22 pm

Cooking in Eulora proves to be a very satisfying way of burning down samovars 1 – alas, they don’t go down in flames and smoke when totally worn-out, but that’s for another day, I suppose.

Anyway, over these last few days, I’ve been boiling molluscs, eggs, shiny rock shards and whatnot – all of rather dubious quality 2 of course. Quality aside, cooking proves to be a surprisingly pleasant activity giving one plenty of time to spare despair of the current state of recipes all scattered about and a pain to find or use on any serious cooking (or crafting of any kind really) spree. So here’s Cookbook version 1.2 3, with most notable additions:

  1. Bill of Materials (BoM) for each recipe. This includes the total of basic resources needed to make 1 (one) item of that kind, from scratch as it were. You can find it at the bottom of each recipe page.
  2. Base value for each item, calculated as the sum of base values of its ingredients. As far as I know, this is how the game itself calculates base values, so it’s fine enough for me. You can find it on the same page as the recipe for making the item.
  3. A “Used in” section for each item and resource. As the name suggests, this is a list of links to other recipes that use the current resource/item as ingredient. Want to know what can you do with a Dead Mollusc? Have a look at the “Used in” section at the bottom of the Dead Mollusc page (spoiler: not much so far – only Mollusc Cheese; still, it’s a very important Mollusc Cheese, seeing how it is further used in Suspect Ointment and Omlette du Disgorge).
  4. Full list of known resources (harvestables) as a separate page, accessible from the index page. Includes: resource name, known location (where available), base value, first finder (when known).
  5. Mining recipes (ordinaries and remarkables) linked from the index page.

As always in Eulora, all the above is essentially work in progress: neither complete, nor with any guarantees, use for your own benefit and at your own risk. As far as I checked it, I couldn’t see anything wrong with the information that is there (although I can see plenty that is missing), so kindly let me know if you find any problems.

  1. Those soon-to-be-a-rarity samovars, since Slithy Tove is STILL successfully hiding somewhere among euloran blades of grass. Or grains of sand, but nothing much bigger than that, in any case.[]
  2. 106 at the moment, as a marked improvement for the below-100 atrocity that came out at first after the most recent overall piss on quality.[]
  3. An upgrade of previous version 1.1[]

December 20, 2015

Foxy’s Dismal Scribblings on Euloran Exploration Data or Truly Horrible Numbers

Filed under: Eulora — Diana Coman @ 1:57 am

Like any other computer game, Eulora is on some level made of numbers. Unlike most other computer games, at this time Eulora seems to be quite often made of not just any numbers but rather truly horrible -borderline aggressive, “give me your money”- numbers.

But you have a choice, as you *always* have a choice, my dearly beloved reader. So here it is, The Choice: either read further, going headfirst into those numbers at your own increasing discomfort *or* ignore them at your own misfortune that will quickly and quite surely come. Without further warning, here is a basic summary of around 4 months of sticking the hoe (and the axe and the pickaxe and even elusive purple snails, scrolls, keys or anything else that came handy) into euloran soil:

    1. Total exploring attempts with *anything* 1: 353`026
    2. Successful 2 attempts: 76`213
    3. Unsuccessful attempts: 276`813
    4. Success rate: ~22% (21.6)
    5. Possible optimistic conclusion: if you dig your 10 toes in Euloran soil, you can take out of it about 2-toes worth of resources. And lose all 10 toes, of course. Cruddy Toes, that is.

    1. Total exploring attempts with tools, namely CH (Cruddy Hoe), SA (Stone Adze), SP (Stone Pickaxe), ICH (Improved Cruddy Hoe), ISA (Improved Stone Adze), ISP (Improved Stone Pickaxe)): 211`819
    2. Successful attempts: 70`104
    3. Unsuccessful attempts: 141`715
    4. Success rate: ~33%
    5. Possible optimistic conclusion: if you value your toes and dig with 10 tools instead, you’ll get about 3-toes worth of resources. And still have the toes, although not the 10 tools, of course.

    1. Total exploring attempts bare handed: 133`869
    2. Successful attempts: 5`784
    3. Unsuccessful attempts: 128`085
    4. Success rate: ~4%
    5. Possible optimistic conclusion: if you think your hands are better than your toes, you are deluded. But if you maintain the same delusion long enough, occasionally it will pay you a little something – possibly just enough to make sure you don’t look for another delusion.

    1. Total exploring attempts with CH (Cruddy Hoe): 187`554
    2. Successful attempts: 63`736
    3. Unsuccessful attempts: 123`818
    4. Success rate: ~34%
    5. Possible optimistic conclusion: Foxy used Hoes way more any other tool, bare hands included. Possibly for that reason, possibly for another, she also got best success rates on it. Where “best” is about three and a half out of ten.

    1. Total exploring attempts with SA (Stone Adze): 9`430
    2. Successful attempts: 2`387
    3. Unsuccessful attempts: 7`043
    4. Success rate: ~25%
    5. Possible optimistic conclusion: none really.

    1. Total exploring attempts with SP (Stone Pickaxe): 9`596
    2. Successful attempts: 2`909
    3. Unsuccessful attempts: 6`687
    4. Success rate: ~30%
    5. Possible optimistic conclusion: Pickaxes are better than Adzes? Or just more used, but in all fairness, Foxy used more pickaxes precisely because of the empirical observation that yes, they seem to be better (at least for Foxy).

    1. Total exploring attempts with ICH (Improved Cruddy Hoe): 4`427
    2. Successful attempts: 931
    3. Unsuccessful attempts: 3`496
    4. Success rate: ~21%
    5. Possible optimistic conclusion: the new definition of “improved” is going from 34% to 21% success rate. Long (and painfully) live improvements!

    1. Total exploring attempts with ISA (Improved Stone Adze): 255
    2. Successful attempts: 33
    3. Unsuccessful attempts: 222
    4. Success rate: ~13%
    5. Possible optimistic conclusion: those numbers are too small to say anything. Pessimists note however that an Adze is an Adze improved or not and therefore good for nothing other than hitting yourself in the head.

    1. Total exploring attempts with ISP (Improved Stone Pickaxe): 557
    2. Successful attempts: 108
    3. Unsuccessful attempts: 449
    4. Success rate: ~19%
    5. Possible optimistic conclusion: numbers are still too small to say anything. Pessimistically, they are just too horrible to say anything, of course.

Note: The lowest level of Euloran quality in writing (or scribbling if you prefer) is “dismal” 3. Which reflects the levels of depth touched -or rather untouched- by this brief analysis. Brief it may be, but it’s still the first ever, not to mention only one to draw on such a large data set and make it public, too! Add your two cents to it if you feel inclined, but perhaps keep in mind the above success rates.

If you made it this far, here’s a picture too:

tools_success_ratio

  1. More precisely: tiny, small and even ordinary enums of all sorts, keys, worthless putrid leather, elusive purple snail. []
  2. Success is defined as “got a claim to show for the effort” – regardless of type of claim or anything else.[]
  3. As in the famous tomes that might even teach you a skill or two: “Foxy’s Dismal Scribblings on Gathering” or “Foxy’s Dismal Scribblings on Building”[]

December 14, 2015

Foxybots v1.3

Filed under: Eulora — Diana Coman @ 11:00 am

A quick update on the bots, mainly for the benefit of new players so that they can get the full working bot with one single compile. There is now a dedicated Foxybots page keeping it all together, including the different versions of the bots as well as a current list of implemented or potential features.

Note that I keep a long list of potential features but I do NOT make any promises regarding their implementation. What is implemented is there and what is not implemented is not there, end of story. Things may or may not change in the future.

And here’s the latest foxybot folder, currently standing at version 1.3:

foxybot_v1.3 – 14 December 2015, sha512sum:

8dbb746617ec18782348ef720d44ac87de3ab67999663919c59f1379e9e46a21c391be568e74f962e4d0aaea96b9dcd97cc24e0f2ee1bec1b99c6ea6f6ee1a47

Steps:

  1. Download the latest client code from S.MG.
  2. Download Foxybot_v1.1 and replace the files as explained there.
  3. Download Foxybot_v1.3 above, check its checksum, run the diff, read it, replace the foxybot folder in your client with the one in the archive above.
  4. Compile the Eulora client as usual for your architecture and enjoy.

Changes in this version:

  1. Added the grid explore strategy. Your character will move to the side for this. Note that on some types of terrain, apparently one move is not enough to count as “different location” and therefore you might get some failed attempts. Nothing to do about this, as the *same* move simply yields different results in different situations. Gotta live with it.
  2. Fixed an issue caused by the reuse of the original client’s code for logging. Lesson learnt re code reuse, there shouldn’t be any seg fault anymore when your log files are very large. Tested this with 20MB files, but let me know how it goes for you.
  3. Fixed an issue in version 1.2 due to some additional spaces in some of the recipes. Made the bot more robust by trimming spaces so that hopefully ALL recipes and containers work perfectly well.

November 24, 2015

Square Data for a Round World

Filed under: Eulora — Diana Coman @ 11:15 pm

Let’s start with an image – for the artists to howl at and run screaming to better places of less precision, predictability and general non-artistry (we actually have them right here too):
gridmap_eulora_island24nov2015

Now that they got bored already, we can see what the above image means. On one hand, it means about 10 hours of “exploring” in Eulora by means of the trusty Foxybot 1. More importantly perhaps, it means systematic, square-shaped and quite fine-grained coverage of Eulora’s roundish island soil for the grand purpose of finding… dead molluscs. And the skins that snakes have shed and slithy toves and even rotten canines of the giants who possibly ate all the sweetness of this land in ages long forgotten and therefore never missed.

It also means – quite possibly – an ongoing battle between rickety reeds (the green army there) and shiny rocks (red), with enemy green infiltrators in the very heart of the mighty red army. Oh noes, cry the shiny rocks and bring in their trusty berries -dressed in yellow- to support the flanks. But mighty reeds against the reds have secret spies of similar colours: a few clumps of dry grass are stuck up the rocks’ bottom, while two-leaf clovers are – quite possibly – the masterminds behind green’s attack, plotting away behind the front lines, in an empty space that begs more digging. The two gray squares are really nothing other than vermin – the swarming grubs that will eat in the end all armies on a battle field, or at least those parts left behind after the battle, in any case.

Anyway reader, I got carried away, can you tell? Faced with the utter order and precision of straight lines that are totally, utterly and quite purposefully ignoring not only the “beauty” of the landscape but its very own structure and everything else to go with it, the mind either goes and does something entirely else for the next few months until the whole thing has been covered *at least once* (covered as in beaten with the hoe, the stick, the adze, not as in truly forced to reveal what it holds on each mm square of its soil) or otherwise begins its own story. An ultimately pointless, useless story, but apparently needed in the moment, quite the opposite of the map above: an apparently pointless, useless map, but ultimately needed if I am ever to actually truly know what resource is where 2. Or at least… was…at the time when I made the map.

Le sigh.

  1. Updated version armed with extreme precision and fancy rituals: each explore is followed by one hop 3 coordinates further and then one spin on the ground – for better positioning really.[]
  2. And I’ll link here for reference the laughter story on this matter, which really goes to show the Universe could do with a kick in its pacademia nuts specifically from me.[]

November 16, 2015

Foxybots v1.2

Filed under: Eulora — Diana Coman @ 12:38 am

The trouble with adopting cats is that they have kittens and the trouble with making Foxy’s bots in the first place is that they have versions. And user requests and changes to adapt to and -possibly- insects. So I’ll just plonk here a kitten trouble – I mean new version of Foxy’s bots:

foxybotv1_2_minimal – 15 November 2015, sha512sum:

01411e6f383a30ae41dac42ebb2c14cac40e1f652776e8dd0550ac39d8c435f147c581bb1c6762ca8504a23e88c5ce9c210cd43532eae431baf1c61b85a0a0dc

Installation:
– download the above archive and check its checksum. Extract.
– run a diff between your current version of the foxybot folder (in src/client/foxybot) and the folder in the archive. Read through the diff.
– either create and apply a patch or otherwise simply replace your current foxybot folder (src/client/foxybot) with the one you extracted from the archive.
– recompile Eulora client as usual.

Changes:
– /bot craft without any parameters will give you the full new list of parameters in the bot’s window. Same for /bot explore without any further parameters.
– (CRAFTING) fixed the issue of “me Gung-ho Gumbo NOT train.” Provided you are not overweight etc, your cooking experience will be trained into shiny new skills.
– (CRAFTING) to avoid targeting someone else’s container and thus potentially interrupting a craft run, bot v1.2 will just lock the container to the one initially targeted by the user. As a consequence, you MUST target the right container before giving the /bot command. The bot will do its best to remind you if you screw this one up, but it’s only a machine after all: it has NO responsibility on this matter or any other.
– (CRAFTING) to avoid failed craft runs, there is an additional parameter called timeout: when that expires, the bot will assume the craft attempt to have failed and it will try to recover by getting everything out of the container and then trying to craft again. Be WARNED: if you give the bot a timeout that is too short, it WILL take things out of the container whenever that expires and as a result it might interrupt a craft and wear down your container. Choose your timeout wisely if you care for your containers (and money).
– (EXPLORING) to allow exploring with any tool you want (yes, even chetty sticks), the explore bot accepts a brand new parameter called timeout (given in ms). Basically the bot will /explore and then wait for some result, but it will check periodically and if more than timeout milliseconds have elapsed, it will just move on. Be warned: if you choose a timeout that is shorter than the time it takes your character to explore with the stick, the bot WILL move when that expires and therefore fuck you with your own chetty stick. Or pickaxe/hoe/adze, as you prefer. No questions asked, no responsibility assumed.
– (EXPLORING) to avoid confusion when others explore in the vicinity, the bot is now a bit more careful about what marker IDs it takes: essentially it will take a new one only if it is either the first it saw after a brand new explore or otherwise if it is the CLOSEST to your character that it saw. Testing this is a nightmare, so let me know how that goes for you.
– (EXPLORING) fixed a small issue related to number of steps: hopefully it’s not off by 1 now anymore, but be warned that this does NOTHING against the imbalance backwards/forwards which is most likely due to differences in terrain really (3 steps up the hill cover less ground than 3 steps down the hill).
– (EXPLORING) used tools will now be stacked in a dedicated slot. As usual, DO put your stack of NEW tools in the first slot in your inventory though, unless you want the bot to burn time going through the same old tools. Your choice though, no problem.
– (EXPLORING) keys of tiny and small claims should now be dropped into their claims (if you did not choose to keep keys in general) regardless of whether the claim was built or not. This is meant still as a measure against the “full inventory” trouble. Also a nightmare to test fully, so let me know how that works.

That’s it, have fun! And bring in the noobs!

October 22, 2015

The Unbearably Unknown Value of Things in Eulora

Filed under: Eulora — Diana Coman @ 12:12 am

If you ever get bored of knowing even vaguely what things are worth in your everyday life, come and visit Eulora. Figuring out what things are worth around here turns out to be a major headache and quite a problem – then again, major headaches and quite-problems are truly the fabric of Euloran reality anyway.

By the way, don’t let me spoil those problems for you. Or the headaches for that matter. By all means, go ahead, dive straight in, bang your head, don’t read a single word further. No, seriously, I mean it, don’t read before play, who ever heard of that anyway? Especially if you have some BTC (or coppers) to burn, go ahead and burn them. I’ll wait until you’re done with that (it won’t be long).

Done? Good, now that you have no money and therefore nothing other to do than to start paying attention finally, here’s what is kind of clear 1 at the moment:

– all items in Eulora have a “base value” which is basically what S.MG 2 uses to calculate the values in its balance sheets (by adjusting for quality, so base_value * quality/100). One can think of this as the “official” value of items since it is after all used precisely in that manner.

– all items in Eulora also have a “quality” which is roughly speaking an indication of where in the hierarchy of skill was the crafter at the moment of making the thing. More useful though, the higher the quality, the more value the item basically stores in itself from the game’s point of view. This is quite important for all the underlying game mechanics, so keep it in mind even if it doesn’t yet have a place in your own mental model (though it should!).

And if you rush in and say that it’s easy then as price things just based on quality, you didn’t understand anything, so go and burn some more BTC until you are truly skint. Then read some logs for a taste of the pricing problem. Read about the trouble of resource allocation and read about some of the rather complex trade agreements currently in place. Yes, you can calculate a value adjusted by quality: base_value * quality/100. Just don’t expect that to always be what the item is actually worth at any given time and in any given context.

Anyway, getting back to the basic value, even though it’s basic, it’s apparently not even known all that clearly or all that easily in game at the moment. So if it’s not in game, come and find it here (values are given in coppers for 1 item of quality 100):

UPDATE (28 December 2015): the list below is an older version, so you are better off checking out the Cookbook which gives you (among other things) base values for all resources and many of the items that can be crafted.

Updated with gracious contributions from Mircea Popescu:

Basic Resources:

BN: 71

CDG: 60

CR: 73

F: 88

NT: 87

RR: 67

RF: 80

SR: 79

Non-basic Resources:

AE: 384

BBB: 197

DCS: 547

DM: 1963

EPS: 399

LH: 721

MD: 1088

PS: 262

SG: 366

SM: 96  157

TLC: 1096

TPT: 159

WM: 780

WPL: 152

WWB: 141

GRC: 5411

PN: 1484

ST: 5093

SS: 1165

Things one can craft:

CFT: 180

CP: 1206

BMS: 540  1589

CC: 3780

CSW: 1216

DG: 470

FCS: 8824

IO: 5439

IBS: 255

LBN: 11

PPB: 4466

PCS: 8824

SCS: 8824

LTF: 4070

SRS: 237 454

Slag: 677

BBBrew: 5354

Almost Wine: 5355 3850

Obj. Screwdriver: 8550

Tube Tea: 10390

Cheap Gin (ACG): 7554 7254

Cheapest Wedding Ring: 6058

The Neckerbocker: 598

BNG: 11395

FT: 5624

TM: 23407

DL: 23877

MC: 15707

SO: 62991

GT: 16394

US: 216655

WD: 51417

 

Kindly add to the list through a comment if you have the base values for other items.

  1. the one and only, Euloran Murky kind of clear I suspect.[]
  2. Eulora’s publisher.[]

October 13, 2015

Hic Sunt Flotsams on Eulora (Or the Brand New Foxymaps)

Filed under: Eulora — Diana Coman @ 11:59 pm

Eulora is this island where all sorts of weird things grow. Or rather grew once, died probably at least twice and now just wait for their lucky chance to be… found. And made into something such as disgusting goop or offensive eggnog or -if they are truly lucky- a drunken wedding cake. You see, it’s not that they have a choice anyway or that they have something to say in the matter anymore. All they have is what they are and so they just lie down and simply wait there, more or less hidden, more or less somewhere, anywhere.

While these things wait (and wait, and wait and…) trees grow and waves bite into the island’s sides like hungry tiny wolves with toothpick teeth but all the time and narrow-mindedness in the world. And so the island changes all the time, ever so slightly, ever so dangerously. Trees and houses and even a cemetery and a ghost appear. Claims disappear. And in all this changing landscape, how do you even begin to know whether what was hidden -even as poorly as under a leaf or under the bushes- has changed as well? Or how do you even know where and how to find it in the first place?

More important yet: where are those bloody lotus harlots already?? I don’t have all day to just wonder around: there is crafting to do (50 samovars were certainly NOT made in 1 day, I tell you) and there are bots to let loose. So I’ve made a map, as simple as that. Not just a map really, but a kind of Foxymap, since it relies on Foxy’s own exploration logs, which in turn rely on Foxy’s bots which in turn rely on …Foxy.

Talk of carrying the world on your shoulders or something. Anyway, here is the map (click on it for a more reasonable size):

map3

Updated: check out the *very little* difference a few days make in terms of map coverage:

eulora_map19Oct2015

And if you want your very own Foxymap showing where you’ve been and what you found there, here’s how to do it:

1. get yourself gnuplot (and awk for that matter, but hopefully you have at least that)

2. go and grab your explorer log file generated by Foxybot (it’s with all other logs, which usually means in $HOME/.Eulora on Linux systems as far as I know). Make a copy and call it testall.csv . Yes, I was lazy and I didn’t bother with a filename parameter for the script. You are very welcome to be so very very not lazy and add such a parameter to the script yourself.

3. download my map script in the same place where you have the testall.csv and check its checksum (updated to correct some rickety mess with reeds and three-pointed thorns):

1246376e8b092c27f982b7fdbd50d337cb18fe8dd7974f2b5f68aa52d19454a4

e9e8a5a7c8c62ae72c6cc82c27fd9ff4cfba70e4eb67670eceb5c68236c063a9

As you can see directly, it’s really a very complex script with a grand total of 1 line per resource. I couldn’t figure out a way for gnuplot to use a third column as color, so I just hacked the log file with awk to get each resource in turn and then plot it as a new series. Rather inefficient I suppose – kindly improve on it if you can.

4. type in a terminal:

gnuplot -persist map1.script

(If you get a very small window, just make it bigger and hit replot button in its menu bar.)

5. enjoy your map!

6. tell me about it.

7. read Foxy’s Diary.

8. do you still need something to do? Go play Eulora already.

September 29, 2015

Yo Moon, Got Phases Nao?

Filed under: Eulora — Diana Coman @ 5:05 pm

(This is Foxy’s Diary on Eulora, at the very ripe age of 20 days. There is a Day 19 and a Day 1 and everything in between.)

shot33

For about one month now, I’ve been walking all around Eulora, gathering anything useful that I could find. For about one month now, that often meant I gathered quite nothing really – I mean nothing other than my own pain, blood and death. I gathered indeed a rich experience of dieing in all places and in all ways, falling off cliffs and just giving up included. I suppose I also gathered some bruises and worn-out hands and knees, but I wouldn’t count those with the useful anyway.

And then, one day, out of nowhere, things changed. I didn’t do anything different really, for there wasn’t anything different to do. But regardless and quite against the good old idea that only idiots keep doing the same thing while expecting different results, it’s precisely those different results that happened! 1. All of a sudden, it started raining claims and finds, left, right and centre. While previously I could wear out whole stacks of tools for a measly few wooly mushrooms, now I would get those wooly devils bare-handed. And I got solid branches too and beetles and two-leaf clovers and even elusive purple snails! At times I even got them by the bucketload as it were, when finding ordinary (not small, not tiny, but ordinary!) claims bare-handed.

Coming from outside Eulora, you might think that there has to be a clear and quite obvious explanation to all this. The pools got bumped up, the rules got loosen down, my skill shot to the skies or something of the kind. But then again, I’ve died too many deaths on this island of Eulora to jump again, naively and unguarded into such trap of the most normal sort. And I’m quite glad I didn’t, for here’s how it goes:

diana_coman: I fully expect mircea_popescu will say that nothing has changed *at all* and there is absolutely no link whatsoever with anything (17:14)

chetty__ hahahaha (17:14)

danielpbarron bare handed is very +ev or whatever (22:33)

mircea_popescu or variance. or the phase of the moon. (22:33)

So now that things are settled and clear and all that sudden bounty is just a glitch 2 and really really totally absolutely nothing at all to do with any change of anything of any import, I think I’ll just keep an eye on that blue inflated thing up there in the sky – that one that never ever deemed to change at all as far as I can say. One day, I’ll get there and I’ll ask the only meaningful question on this topic:

Yo moon, got phases nao?

And then I’ll blow that moon up once and for all, phases or no phases. I’m just a romantic like that, just looking at the moon all day, you know.

  1. Does that make Eulora the most idiot-suited land? I guess it depends – on the phases of the moon, most probably.[]
  2. The second one at that, for there was another one a few months ago when I foolishly did not make a note of the exact day to have a proof of it.[]

September 3, 2015

Pulling mushrooms by their wooly threads and other disastrous adventures

Filed under: Eulora — Diana Coman @ 5:51 pm

(This is a horrible 19th day in Foxy’s Diary. The others are even worse.)

Last night I hunted for wooly mushrooms. Shrouded in darkness (since my clothes are just a bunch of rags) and with my trusty cruddy hoe in one hand, I moved and I plodded, I scratched and I prodded. And once in a while I caught a mushroom by its wooly wooly threads that gleamed white in the night. Oh, how it twisted and it tried to get away. And how I couldn’t care less about it, as I just shoved it into my sack and I moved on to the next one.

By morning I had a good hundred mushrooms and a few to spare. And I also had a load of data, even more interesting than the shroomy creatures themselves. Here goes:

– A run of 4399 tries (gotta love the number, eh?). That’s 822613 cruddy hoe “hit points” spent right there, or in other words, about 82 hoes (each try wears down 187 quality points out of a cruddy hoe). I start getting those old stories in which they were eating three stone breads and wearing down three stone sticks until they got where they wanted.

– Overall success rate is 53% (2344 successful grabbing of WM by their wooly beards, SM by their mossy hairs, a few AE by their shiny shells and all sorts of other stuff too). I’d say that’s not bad at all for rare items such as these, but feel free to disagree. I’ll enjoy it even, please do.

Update: to make it clear, the above are totals on a full-length file of mining logs. While they stand fine on their own, the WM run is shorter, namely made out of 2344 tries (approx 44 tools calculated as 2344*178/9352 since each tool had quality 9352 and it was worn-down by 178 points per try). Out of those 2344 tries, 2055 failed and 289 were a success for a much more horrible rate of success, of course. Those 289 successes were 21 small SM+23 small WM +3 tiny AE+136 tiny SM+106 tiny WM, just as previously noted below:

– Found 117 WM, 236SM, 3AE, all of them around 169 quality. If that’s a quality really, not sure there.

– The above were extracted screaming, hissing and spitting from the following claims:

– 23 small WM and 106 tiny WM (small:tiny is approx 1:4)

– 21 Small SM and 136 small SM (small:tiny is approx 1:6)

– 3 tiny AE

– Quite as expected, small WM claims are small in gains rather than anything else, hence 27 WM were gotten out of 23 small claims. It could have been worse, of course, always, sure, no question about it. Wolves were hiding in the mountains, things were screaming in my mind. Still, small WM claims suck, there you are.

– The SM is slightly better behaved: 118 SM gotten out of 21 small claims. Curious though how I got a worse ratio small:tiny on SM. Probably those wolves howling or something. Scared any sense out of it all.

As for a bit of cost calculation, things are quite muddy given the mix of WM and SM. Still, considering values adjusted for quality, that would be 800k 440k coppers burnt (those zeroes will kill me one day) for 154k worth of WM , 38k worth of SM and 2k worth of AE. That’s a wonderful loss, isn’t it?

I’ll leave it there. I did not even add the cost of thread or of the increasingly rare bits of nothing.

In similar ways, a different long run of 110 tools gave me 352EPS out of 47 small and 229 tiny claims, as well as some change in the form of 20WM and 42 TPT. At base value adjusted for quality, that’s roughly 280k return on 1100k input.

Still wondering why do EPS cost 1k or more?

August 30, 2015

Eulora’s First Grade – How to Get a Skill, Build a Claim, Make Your Own Grass etc.

Filed under: Eulora — Diana Coman @ 11:06 pm

(This is Day 18 of the one and only diary that Eulora has. Day 17 was about pink harlots.)

Since new people are both sorely needed and otherwise cluelessly thrown into the world of Eulora, I found myself teaching Eulora’s First Grade as it were. Teaching might be good, but repeating is absolutely pissing me off, hence I have to write it once instead of saying it 100 times. So, once you got into Eulora and got your starter package from Mircea Popescu 1, here are a few things to do:

– “Use” the smelly murky something and the mysterious rock (and the chicken scribblings if you have them) in order to get the respective skills (read the wiki if you don’t know which skills). To “use” an item, the simplest way is to drag and drop the item onto your character in the inventory window.

– Check your shiny new skills: in the stats and skills window, you should have at least Gathering under the Gather tab and Building under the Utility tab.

– If the bar of a skill is red/yellow in the stats/skills window, you need to train it (so that you don’t lose experience (practice points) and you can rank up). Go to Heina Draggenfort – the NPC closer to the public craft table in the “village” – right click on her and click on the scroll-like icon (train). You’ll need to select the skill, choose 100% and pay. Yep, you need money (copper) too. Have none? Talk to Grundin in game/ jurov in #eulora chan.

– Once trained, you can either go around and just explore (type in the text window: /explore) to find things yourself, or (much faster way of ranking up) look for someone with higher skill who is willing to give you claims to build for them. I’m currently offering this deal, so whisper to Foxy in game (/tell Foxy ) or diana_coman in chan.

– When you got a claim (by whatever means), go next to it, right click on it (it’s a stick basically and a rather narrow one at that, so yeah, aim and click) and “examine” it (the eye icon). That will open the claim which is basically a container so it has slots for you to put stuff into. Put in there whatever the claim needs to be built (1 bit of nothing for a tiny claim of any kind; anything between 1 and 7 coarse frangible threads for a small claim of any kind). Put the corresponding scroll (tiny ennumeration for a tiny claim or small ennumeration for a small claim) on the brain in the inventory window (just drag and drop on the character in inventory and it will go to the Mind slot anyway). Then click on “use” for a tiny claim or “combine” for a small claim (buttons on the left-hand side of the claim window, first from top and second from top respectively). Watch it transform the things and enjoy your very first built mine!

– Check your skills again as it’s quite likely you need to go and train. Repeat (better yet get yourself a bot or macro or anything to do the work for you) and enjoy!

And here’s me with some of my Euloran students + a bunch of sticks (or claims by their glorified names), as well as an opened claim (second image): the “hand” icon is the “use” button for the claim.

shot30 shot31

  1. Update 28 November 2016: starter packages and skill items are NOT given anymore but you can do everything even without ANY skill whatsoever[]

August 26, 2015

A Lotus by Any Other Name’s…a Harlot?

Filed under: Eulora — Diana Coman @ 10:35 pm

(This is Day 17 of the Barely Legible and Slightly Bloody Diary of Foxy Foxster on Eulora. Read all about it! I mean: here’s Day 1, there’s Day 16.)

The one thing that you can truly find in great – even generous! – quantities on Eulora is failure. Usually yours to keep and others’ to enjoy in a truly community-like spirit that seems to be increasingly abundant too, for all the very ripe age of 17 days I actually got to write in this diary. And failure on Eulora is so abundant that even the soil gets it, even the trees, even the grass, even the rocks. I’ll not even mention those hills that failed to be mountains and that sad sad sand that failed to remain a rock, a shell or anything of any description whatsoever.

Still, on rare occasions, on Eulora, even failure fails. And so you get surprises – truly surprising surprises of the surprisingly pleasant kind. Such as actually finding true and shiny pink in a drab world of mud and brown, such as flowers out of failed soil, such as lotuses in the desert. Such as looking into your own bag after a long mining run and seeing there, unexpectedly and cradled among all sorts of wooly mushrooms, abandoned eggs, spicy moss and sad looking clumps of dry grass, the pinkest pinkish pinkies pink flowers that actually look like…flowers! And not just any flowers at that, but Lotus flowers!

Oh the joys of the lotus, the joys of the pink the true joy of the pink lotus that grows on Eulora! On another island one might have called them Pink Aphrodites or something of the kind, but on Eulora the song fails, the muses hiss and curse between their broken lips and mutter with mischievious delight the words to name the wonder of a flower: it’s Lotus Harlots on Eulora, the flower that looks like you might think it does. And the flower that sells of course, for the right price, for the high price, for the one who pays the most.

There will be Harlots at the auction on Sunday. I mean: Lotus Harlots!

August 14, 2015

A Data Diet of Wooly Mushrooms and Elusive Purple Snails

Filed under: Eulora — Diana Coman @ 5:54 pm

(This is Day 16 of Foxy’s Diary or BLSBD by FF according to Euloran naming habits. You can start with Day 1. Or not.)

As I unleashed my barely hatched mining bot on the lush hills of Eulora, it ate a few hundred Cruddy Hoes, about 1000 Little Bits O’ Nothing and around 500 Coarse Frangible Threads, spitting in return …some data. Sorely needed, totally missing and otherwise rather utterly depressing data. But needs must and hunger makes a meal tastier, so I reluctantly made a few basic calculations:

Wooly Mushroom (WM) mining cost: approx 3500 coppers per 1 WM

– based on a mining run of 10 hoes from the merchant (quality 9352) that resulted in 29WM

– considering I paid approx 12k to the merchant and spent tools currently still fetch 2k: 10*(12k-2k)/29 = 3448

! Note that costs incurred for thread and bits of nothing and time used for the mining are purposefully ignored in a rather optimistic assumption that they are compensated by the experience points also gained in the process.

Magical Dungbeetles (MD) mining cost: approx 8000 coppers per 1 MD

– based on a mining run of 26 hoes (q 9352) that resulted in 33 MD

– using same assumptions as above: 26*(12k-2k)/33 = 7878

Note that neither MD nor WM *ever* gave me anything better than a small claim when mined with basic or improved tools, despite having mined them for more than 3 days and 3 nights together. I got both ordinary and even 1 remarkable claim on WM though, when using a chetty stick.

Update: Clump of Dry Grass (CDG) mining cost: approx 72 coppers per 1 CDG

– based on a mining run of 2 hoes (q 9352) that resulted in 275 CDG (from 10 Small claims and 88 tiny, so ratio almost 1:9)

– using same assumptions as above: 2*(12k-2k)/275 = 72

Ratios of small:tiny  (on a rather tiny sample)

BBB:  1:2.4  (based on 27 total)

RR: 1:6.4 (74)

SR: 1:8 (27)

TPT: 1:7.33 (50)

WM: 1:8 (27)

WPL: 1:19 (20)

Data

CSV file with claim found and output from each claim: here. Feel free to use it in any way and share (or not) as you think it’s fine.

August 3, 2015

More offensive than ever – Euloran Cookbook v1.1

Filed under: Eulora — Diana Coman @ 10:49 pm

(This is Day 15 of Foxy’s Diary or BLSBD by FF according to Euloran naming habits.)

Despite my love of samovars, it turns out that I can’t cook worth a shit, since I am missing the all-important and more-elusive-than-the-elusive-purple-snail Gung-Ho Gumbo skill. (Yes, it sounds like a very noisy jumpy-pokey dance, but I didn’t name it myself.) So what do you do when you can’t cook?

You sit down and write a cooking book, of course. A more-complete-than-ever and utterly delightful cooking book.

Or in my case, re-write a cooking book. So ladies and gentlorans, I give you Foxy’s Euloran Cookbook V1.1, with about a ton of modifications including:

– final, stable page names; feel free to link to any of them, as I won’t change them unless Euloran Gods change Offensive Eggnogs to Inoffensive Eggnogs. Phat chance that might happen, you say? I thought so, yeah.

– about a ton of new recipes added with all their disgusting details. Can’t find the recipe you know, have, love, treasure and in any case don’t want to sell under any circumstances but would like to see given to you for free by someone else? Talk to me in game, show it to me in game and it will magically appear here too.

– new information added for each recipe regarding the container needed (such as craft-table or samovar) and the tool needed (such as penance clogs or chair for the head). Where there is a blank space at the tool, that’s not an error, but a feature: there is no tool needed, don’t complain, but enjoy!

– summary pages added for each container. Want to know just WHAT can you do with your brand new, shiny and hissy samovar? Check it out in Foxy’s Cookbook V1.1 on the Samovar page (you’ll find it from the main page, I won’t link it here, no way.)

 

July 31, 2015

Solid Branches in Empty Space (Eulora’s First, Woman-Made, Satellite)

Filed under: Eulora — Diana Coman @ 10:08 am

(Day 14 of Foxy’s Diary)

That’s how women are really: they make technology out of anything and everything. Give them just a bit more solid branches than the usual flimsy flotsam and next thing you know the whole planet has a satellite. And functional teleporting. But let me start from the beginning.

As I was happily getting my hands on something solid for once in Eulora – solid branches I mean – I became all of a sudden overweight. Not the “I ate a whale and a dolphin for breakfast so now feeling kind of bloated” kind of overweight, but rather the much more boring (and profitable) “I carry too many branches to even move” kind of overweight. And what can you do if you carry too much? Drop something of course, preferably onto solid ground next to your left foot and at a safe distance from your toe. And so I dropped it. All of it, my shiny new (and did I mention quite valuable?) pile of solid branches. I dropped the whole pile as that’s what you do when you carry too much, don’t you? You just drop it.

Oh, boy, I surely dropped it, all right.

I dropped it ALL THE WAY TO SPACE! Yes, you heard that right, the first satellite that Eulora ever had was my pile of solid branches! Talking about rocket launchers and all that jazz when I can put things into orbit with my bare arm!

And so I looked in horror at the empty place all around me where NO solid branches could be seen or gleaned by any means. I jumped and I crouched and I mumbled and tumbled and surely grumbled 1, but the sand remained bare returning quite frankly its emptiest stare. Upon craning my neck at impossible angles and using my third eye to see through the sands and under the water, I did get a glimpse of the branches buried so deeply that they had gotten out onto the other side: a bag of solid branches in empty space and nothing more:

Solid branches in empty space

Since I’m not one to stare for long at unresponsive thieving sands, I threw away the mining tools 2 and got out my hacking kit with its teleporting powers. And what do you know, a few teleports into the ground and into space, a few clicks and a few curses (well, more than a few, but you get the point) later I had grabbed my solid branches back from the empty space.

In much less time than all that teleporting and grabbing thing, I had done even better than grabbing branches from outer space: I had sold them for a profit! And taken a picture of the whole island for mapping purposes:

shot24

Now that’s why magic is great. Because it solves your troubles AND it pays handsomely. What else could you even want from it?

  1. That might have been a curse or two. Or more, I didn’t count really.[]
  2. To a locked claim this time, no more dropping for me, thank you. []

July 27, 2015

Foxy’s Crafting Bot

Filed under: Eulora — Diana Coman @ 6:50 pm

Day 13 was a very long day indeed. Not sure it’s even over yet for that matter, but maybe in a few days…

Still, Crafting Bot is here. Version 1.0 with a messy structure still (due to problems with missing messages from the server and other unsavory aspects of the original PS client) and some still-to-be-resolved issues. If you want to give it a try, here’s how it goes:

Download: the bundle.

Check its sha256sum: 6fd93088108658e622e452278da62d209f5ac82f8169ab2253175f423de6851b

Unpack the files, read the readme.txt provided in the bundle and follow the steps there.

Recompile Eulora client (incremental compile is fine if you are in a hurry, no need to recompile all from scratch really).

An alternative bundle is provided on Trilema too.

July 5, 2015

Day 12 – A Drunken World – Drunk on Knowledge That Is!

Filed under: Eulora — Diana Coman @ 4:39 pm

(This is Day 12 of The Barely Legible and Slightly Bloody Diary of Foxy Foxster on Eulora – you can find previous days, but only if you really look at what you’re reading around here.)

You know that feeling of being on top of the world and not giving a damn where you pee since it can’t possibly pee back? Well, it turns out it can and of course it does it, really quickly. Just as I was getting drunk on tubers tea (well, let’s call it that), the world was also getting drunk on new skills and knowledge as it were. Trouble is that it got first more drunk than more new skills and knowledge. So it started taking tumbles and playing ball with all sorts of things – mainly other people’s expectations that is. Uhm, maybe it just got a temperament instead of too many drinks?

Anyway: first, the quality of mined items went all crazy and lost so much heart that negative numbers suddenly seemed a possibility. Then, a most remarkable claim made the most remarkable feat of vanishing before one’s eyes, locked and all, bye-bye loot hello poverty or what was it? A trade went crash and then the bubble burst, taking the world with it in a swirl of madness. Called upon the scene of eulorian tragedy, Chetty took a look and cried: “look at the state of that! I’m taking it offline!” Uncalled by anyone but totally preoccupied by the fate of the world at large, I cried instead: “that’s my shiny new remarkable claim, right there!!!!”

Several hours later, I got first the bad news and then the good news. The bad news was that my claim was no longer to be, snatched by the angry gods of fair-play under the heaviest suspicions of fraudulent birth by means of drunken world’s vomit or some such. A bit later, the good news was that the world might have vomited my claim indeed, but it did so in total fairness and in a state of being legally not-drunk-at-all and therefore I got to keep my claim. And to wake up in a brand-new, wiser world who all of a sudden got more skills that I knew or could access. Hmm, did I say good news or bad news?

Oh, and at some point I was floating in the air surrounded by claims. How’s that for an ending in 1000 words?

shot16

June 30, 2015

A Thing to Be Treasured or the Secret of Eulorian Samovars

Filed under: Eulora — Diana Coman @ 8:59 pm

(This is Day 11 of The Barely Legible and Slightly Bloody Diary of Foxy Foxster on Eulora – you can find previous days in which I talk about things of wonder and golden pickaxes.)

I certainly mentioned my wondrous eulorian Samovar before, the multi-functional and totally blessed shiny hissing thing that might get me one day to the moon. And if it didn’t yet get me there, it surely did today a thing that is even more surprising for a tool of this island: it actually made tea! Eulorian tea, totally tea, unexpectedly drinkable tea! Still, I suspect it simply felt it had to make tea just to spite me, since I publicly and oh, so assuredly wrote before that it did anything BUT tea.

So on this day, the 11th, I brew my eulorian tea from as many as 36 tubers, 7 wooly mushrooms, 2 spicy moss and one pacademia nut. I sat then on top of the hill next to my now-gently simmering samovar and I admired the view over Eulora’s green and rocky hills, between the sticks of claims and with a flikering silvery fishy fountain in the background. And as I sipped my wondrous tea of tubers with a pinch of spicy moss and pacademia nut, I felt for the first time an unbearable lightness of being on Eulora. After which I checked my pockets and I discovered that they were indeed quite unbearably lighter, some hundred thousand satoshis lighter.

A cup of tea on Eulora is a thing to be treasured indeed since it is basically a whole lot of money with a dash of hot water. The secret of Eulorian Samovars must surely be that they can quite actively transform a lot of money into very little liquid…

shot14 shot15

June 29, 2015

Things of Wonder and Golden Pickaxes

Filed under: Eulora — Diana Coman @ 2:43 pm

(Days 9 and 10 of The Barely Legible and Slightly Bloody Diary of Foxy Foxster on Eulora – you can find previous days in the teleportation samovar.)

Day 9 – High-Quality Wonders Are Solid – Rock Solid!

The first polished stone I ever got was a thing of wonder. In this murky, drab and demeaning world, it shone in the sun with the quiet perfection and calm stilnness that only years of resting at the bottom of a riverbed can impart 1. As I hadn’t been resting for the same time though, I was possibly less calm, less still and less perfect when I grabbed it from the murky soil in which my exploration had uncovered it. I welcomed its perfectly smooth surface against my worn out hands and I smiled with delight when I felt the slight pressure of its weight and I noticed the high quality of its making: it might have been a little stone only, but it was a solid, high-quality one!

Truly, for something gotten out of a bit of digging and a grand total of one single coarse piece of frangible thread, the first polished stone I ever got was indeed a thing of wonder: it sold for 461 coppers and if that’s not a wonder on Eulora, I really don’t know what is.

Day 10 – I Really Can’t Decide on a Title: I Can Mine Like No Other/ My Pickaxe Is Better Than Yours

How Does Your Fortune Grow?

If one polished stone is a thing of wonder, several polished stones must be a cairn and that’s “a whole lot of coppers” in Eulorian. So no, I haven’t yet found cockle shells and silver bells 2, but I’ve found Mod (Seis 3 ). And Mod found what was for him a teeny tiny polished stone, but proved to be for me (and my golden pickaxe) quite honestly a cairn in making.

Mod is a fellow eulorian and as a consequence he’s quite as bare-footed and bare-handed as everyone around here is. He’s been around for a while and we previously exchanged bizarre items such as wooly mushrooms and copper coins, but this time we quite hit on something good. For you see, Mod has a knack for finding small claims of polished stones all around the rocky area where the merchant lives and the craft-table awaits. From these claims, Mod would extract on his own one polished stone here and one polished stone there, all chipped up and dull and tiny. So tiny in fact that they would barely fetch him a few lonely coppers that might not even pay for the pickaxe that he broke digging around. But when I search instead these claims that Mod found, I find big, shiny, polished stones that pay more than double. And so, for the starters, Mod passed me the key of a teeny tiny claim from which he would have normally gotten at most 278 coppers. I dug the claim up and I got as much as 1383 coppers in no time. Even splitting it evenly between us, that still left Mod with more than double his copper coins.

I guess that’s how your fortune grows: by doubling the money you get for the same action. You just need to trade your claim to someone with higher mining 4 skills and then watch that pile of coppers (satoshis really) growing. So, how does your fortune grow?

  1. That’s largely valid only if you are a stone, of course.[]
  2. I’m quite surprised they are not on the island really, at least not in their darker, slightly more hidden meaning.(On another island, there was a nursery rhyme that went Mary, Mary quite contrary, how does your garden grow? With cockle shells and silver bells and maidens in a row. The hidden meaning (if any) being that Mary was Bloody Mary, silver bells were thumb screws and cockle shells were more like… cock shells I suppose you’d call them.) []
  3. and that’s 6 in Eulorian[]
  4. Mining is currently called building on Eulora, possibly because you mine the buildings or build the mines or otherwise simply put you just dig anyway and for all purposes, as you don’t really know how to do anything else.[]

June 25, 2015

Samovars, Boulders, Business and Teleportation – All in a Few Days’ Work

Filed under: Eulora — Diana Coman @ 11:01 pm

(These are Days 6, 7 and 8 from The Barely Legible and Slightly Bloody Diary of Foxy Foxster on Eulora – you can find previous days with lemonade, flotsam and horribly bad Maths.)

Day 6 – Can You Fly to the Moon Riding on the Samovar?

Making a samovar on Eulora is quite the state-of-the-art in the bunch-of-chairs-for-the-head crafting: by the time you have the bloody hissing thing, you shaved a ton of barkings (or barked a ton of shavings, what was it?), fashioned slag to be both pointy and flat (possibly also pointy-flat and flatty-point as well as flatypus or platypus) and destroyed a small pile of toolkits, chairs for the head 1, keyboards and possibly mice. But all is well when you add disgusting goop, for you got a brand new and shiny samovar! Which is also multifunctional! A kind of iSamovar I suppose, since it surely makes a lot of things you never thought you needed on Eulora (even toothpaste!), except tea, of course.

So having spent a few hours to make my iSamovar, I barely got to use it for making a batch of toothpaste, when the party was on. All those people dragging bandar toolkits and stone pickaxes, while I was wearing a kilt of rickety reeds and holding toothpaste…. Still, I hid the toothpaste and got my Samovar blessed instead. Now it’s blessedly multifunctional, so it’s surely not too much to ask to be able to ride on it all the way to the moon, is it?

Day 7 – Foxy’s Legendary Boulders (and Legendary-to-be Shop)

Having established that the Samovar is mainly good for toothpaste and the toothpaste is mainly not good for anything, I chucked all of them into storage and I went digging in the bushes around instead. It’s always far better to dig in someone else’s bushes, especially if you have a stone pickaxe and a rather high skill at what you’re doing (some say it’s looting but shhhh). I sincerely did not have big hopes for it, since I had dug in the same bushed before without much to show for it other than bloody hands and a few deaths.

Still, mining is business I heard and business is business, especially in the bushes. So I paid through the nose for slithy toves, polished small stones and three pointed thorns, after which I mixed the whole lot up and looked away. When I got back, I was under a pile of boulders. More precisely 15835 of them! Quite the find, quite the pile of money, so no, I’ll not complain about it. Whoever is with too much money, let them throw first the ton of boulders at me, or how was it?

After such success, the next mining was of course a total flop and therefore I moved onto other things. More precisely, I decided that mining-business is business but not exactly my business or for-this-very-moment-business and so I started…business. That’s how Foxy’s Useful Items shop was born, selling very useful items indeed, such as ready-made high quality Bandar Toolkits, the ever-needed-in-the-ton-for-the-samovar-and-bottles Indistinct Bark Shavings as well as Chairs for the Head. Plus pretty much everything else, if you just ask for it. And pay, of course. Did I forget to say it was a business?

Day 8 – Did We Invent Teleportation Yet (with a Samovar)?

As you can probably tell by now, the Samovar is nagging at me. The call of the Samovar is strong on Eulora, especially after having visited relatives who actually have not fewer than 3 actual, real, functioning samovars. Plus a normal modern kettle that they actually use for tea. You see, all samovars are multifunctional it seems, since a very traditional shiny samovar is used mainly as coin-holder and piggy bank, another strange-looking dwarfish samovar is used as ornament and a third kitschy shiny plastic oriental samovar is used mainly for… taking up space? I don’t know, I should ask next time. Nevermind, going back to my own samovars which unfortunately are not useful for teleporting. Damn.

Undetered by the weaknesses and lackings of my samovars, I started rummaging around in the client’s code instead, where things truly are more disgusting than any Eulorian Goops, more useless than local toothpaste and possibly, occasionally, more demeaning than any Hoof Gloves and Urabihs harnesses. Still, not as incomprehensible as a Chair for the Head I guess. And lo and behold, in almost as little as it took me to make that blessed iSamovar in game, I could actually make the unblessed, unapproved, frown upon and soon-to-be-axed change to the client so that I can …teleport! Anywhere, even to the moon, even under the island (no, there are no elephants and no tortoises there, sorry), even under the craft table. Here are some photos for you:

shot08 shot09 shot10 shot11shot12shot13

  1. better known as head-stools for otherwise an oh so important shade of.. brown is surely lost. Please don’t lose it here, please.[]

June 22, 2015

Godly Yellow Lemonade, Loads of Flotsam and Horribly Bad Maths

Filed under: Eulora — Diana Coman @ 12:09 pm

(The Barely Legible and Slightly Bloody Diary of Foxy Foxster on Eulora – Days 3, 4 and 5 when only horrible things happened. Day 1 and Day 2 were pure bliss(ters) by comparison.)

Day 3 – When Gods Take a Piss, You’ll Make Very Yellow Lemonade Out of Everything

Just when I thought I was getting the hang of cooking disgusting dishes, spinning threads and smashing rocks, a mighty gust of gods’ disgust (and possibly yellow piss, too) blew all over Eulora. As a result, the disgusting became the unmentionable and all quality was disgusted out of all items made by eulorian hands. So much for training, so much for learning, so much for going to sleep with a certain fragile peace of mind that insists on lying to you about the world being there just the same when you wake up. Well, it might be or it might not be – who knows what god lacks that night both basic indoor plumbing and the very humble potty of his ancestors.

I am still undecided on whether to call this event the Big Drop or the Big Piss. It certainly did NOT come with a big bang, so that’s totally out of the question. Perhaps the Big Splash.

Day 4 – Flotsam Is Your Friend

Today I made friends with a whole load of Flotsam. No, I’m not going insane and I did not hug the damned thing: I just dug it up and carried it to the merchant, as it sold for very good money, especially as it wasn’t a long way to carry – just dig behind the rocky area where the merchant lives and there you are, lots and lots of flotsam claims. Click, click, click and there you are with a beautiful Carpal Tunnel Syndrome stack of flotsam ready to sell. Convenience, money and a short trip to get both – what more can you want from a friend, especially on a land such as Eulora?

Day 5 –  I Have an Interest in You – Compound Interest That Is

Do you know that lovely tall lady oh, so nice and knowledgeable? That lady standing a bit aside from the merchant and giving you the kind looks as if to soothe your nerves that got so tense from the merchant’s nasty and oily ways. Yes, yes, that one – the Heina girl, who is a trainer through the goodness of her heart and the knowledge of her soul, the one and only trainer Eulora has seen so far, the one and only human being to really look not only at you, but deep inside you, taking an interest as it were in your very you and your development.

Well, it turns out she took an interest all right: a compound interest. All this time she was scamming poor newcomers charging them 10 times (or more) 1 the fee for teaching them a measly level up on exploring, tinkering or building. She’s been exposed now and she quickly turned around with a brand new offer of training services at decent – almost tiny –  prices. Who knows for how long that’ll last?

So beware of Bad Maths. And of people who take an interest in you – it might be a higher interest than you know…

  1. Approximately 400 times in fact, as later revealed by those who really, truly, simply, know.[]

June 14, 2015

When Cooking Is More Painful than Dying (The Barely Legible and Slightly Bloody Diary of Foxy Foxster on Eulora – Day 2)

Filed under: Eulora — Diana Coman @ 8:59 pm

Since Day 1 was over, I have to assume Day 2 has started.

For the whole morning I explored Eulora with enthusiasm and bare hands, which always makes for a rather painful combination. Although my enthusiasm survived quite a lot of eulorian exploration, my hands were soon raw and I was shaking all over (or so it was written all over the place). And it was so that I found not only flotsam, birds’ nests, crumbly rocks, rickety reeds and clumps of dry grass, but also the fact that death on Eulora is just about the best thing that I can wish for at this time. Unlike most other things one can wish for, death is something that can even be gotten quite easily for the time being: I just typed /die and watched myself becoming still for just a moment before a new rush of ….life? came in pumping quick blood through the veins as if it was a miracle all new, as if nothing else existed before, as if life itself was a new wondrous invention that could never fail in any way.

And so at noon I learnt to die, but then I went on and learnt to cook the Eulorian way – something really more brutal than death itself judging by the name of ingredients and recipes: disgusting goop, rotten fruit, sharp clump of slag, coarse thread and hoof gloves (seriously, what goat came up with that design?). Making goop out of crumbly rocks, rotten fruit and spicy moss was quite a feat, although the quality part was certainly more confusing than the recipe: is a higher-quality disgusting goop more disgusting or less disgusting or simply differently disgusting than a lower-quality disgusting goop?

Quality issues aside, I started a cooking book. If it proves to be of any use, I might add to it one of these days, in between dying, cooking and tinkering…

June 13, 2015

Names, Pains, Curiosity and Deliberately Bad Maths (The Barely Legible and Slightly Bloody Diary of Foxy Foxster on Eulora – Day 1)

Filed under: Eulora — Diana Coman @ 10:25 am

shot07

Day 1

On Day 1 I came onto the shore as if born out of seafoam, all beauty and glow, as Aphrodite was my name – ha, ha, all diaries should better start out as a joke to have even the slightest chance of not ending as one. So no, that’s not exactly true, nor could it ever be true in such a place as Eulora, where everything is dirty, murky, bloody and at best primitive in the most disgusting and demeaning possible meaning of such a word. So let me start again:

On the first day, I crawled on my knees barely making it onto Eulora’s shore, my eyes burnt by the sun, my skin lashed a thousand times by treacherous waves and my brains boiling under the unforgiving sun. Still, I was breathing, I was feeling the hard, abrasive sand of the beach under my feet and I had therefore won my chance to live. A slight and elusive chance, granted, but a chance nevertheless, and chances were just about everything I had left, since upon inspection I could not even find any memories I could call my own, let alone any concrete objects or possessions.

To that sliver of a chance, I soon added a name and I made that to be Foxy Foxster. Given the circumstances, it was surely better than Aphrodite and it increased the full total sum of my possessions to two abstract notions. I think that was not too bad for such a painful and deliberately down-to-sand beginning. Still, names and chances do not a life nor a full belly make and therefore I just started walking (or should I say limping?) around. It turns out I had already messed up even the most basic of calculations, since I should have added both curiosity and growing pains to my list of possessions. Give the woman a slight chance at life and she adds names, pains, curiosity and deliberately bad Maths to it in no time, even on a deserted island, such as Eulora.

Just a few steps ahead, I came upon the first proof that Eulora was not exactly deserted: there, on the scorching sand right in front of my eyes stood a woman looking exactly like me. Her name was Sir Reginald Greyhawk and she spoke mainly nonsense, but she did it in style, with certainty and looking you straight in the eye. Besides the nonsense, she also gave out restoring bottles of booze, so overall a relatively good start to life on an island I suppose.

Marching ahead past Sir Reginald and onto a bit of grass, I came to an odd structure going by the name of Lady Voyna’s Lean-to. Nothing of interest there really, but it served as a good landmark, so I drew it on my mental map and then turned right from there, walking parallel to the shore. I didn’t have to walk too far before coming to a few trees where I found Chicken Scribblings. Those things can’t be described in words for what they truly are, but the important thing to remember is that they are your first mentors on this island: read them and you’ll learn Tinkering, a most useful skill.

I kept walking then in the same direction, parallel to the shore and for quite a long time until I got to a place where the shore came down very low between two sand hills on either side. There, at the bottom, cradled between the two sand-buttocks of the shore lay a bottle of Smelly Murky Something and by this time I could not care less about murkiness or smelliness or even somethingness so I just plain and simply drank it all. Unsurprisingly perhaps, the murkiness in the bottle actually cleared my head a little or so it said and then and there I remembered the most basic things of them all: how to explore by looking, digging and generally poking around. A most useful skill when you have none other.

There, on the sandy shores of Eulora I waited and waited for another Smelly Murky Something to come to me, for my thirst of knowledge was great and the bottles were small. And I admired quietly the blue moon rising, after which I drew a line in the sand and stepped over it and into a new day

August 20, 2014

Making My Eulorian Cake and Eating It

Filed under: Eulora — Diana Coman @ 12:03 am

level10

Once Boxy found his crafting purpose, he followed it all around the small land of Eulora and then some more. For he had nothing else to hang on to when the void howled through his numbered holes and the empty plains did not bother to move even a single blade of grass as sign of his passing. And so he crafted leaky flasks and metal studs, crude wineskins and berry tinctures, alchemists’ gin and berry tea. But his crafting gave him no joy, for the recipes crumbled in his hands just as he crafted what they described, the objects were nothing more than pure consolidated misery in their brownish, dilapidated state and his holes seemed to have gotten only larger if nothing else. Because before Boxy even knew it, he had acquired not only a purpose, but also a need that was becoming more obvious with every object crafted: the need of knowledge.

Like many other creatures before and even after him, Boxy sought knowledge at first everywhere – everywhere except in the right place, that is. For everywhere was quite conveniently ill-defined and allowed the very comfortable idea that knowledge will simply come to him. Sure, in his generosity, Boxy allowed knowledge to even require some kind of rather basic physical effort on his part. Such as, for instance and quite simply for example crafting, since crafting was something Boxy had already figured out. Alas, knowledge was thoroughly unimpressed with Boxy’s generosity. And so it simply did not come to him, no matter how many gin bottles Boxy mixed, no matter how many metal studs he hammered and how many berry teas he boiled. But then, in this process of enticing knowledge through quite simply the only means that was totally ineffective, Boxy discovered something else, something with icing on top: a cake!

The cake looked surprisingly colourful and even good, despite its frankly disgusting ingredients, which only goes to show that there is something truly deceitful in the very nature of cakes. But if it looks like a cake and it smells like a cake, it has to be good as a cake reasoned Boxy and his reason barely had time to say cake a third time that Boxy had already eaten 5 or 6 of them. Somewhere, in green letters on a black background, something told Boxy that he felt “odd” after each cake, but then again, what with his cubes and his number-shaped holes, Boxy had never felt anything other than odd, so that did not bother him too much. What bothered him more was the fact that so many cakes in so little time had made him… thirsty.

Boxy was thirsty for knowledge that is, so thirsty that he finally gave up trying to convince knowledge to come to him. He went instead and got knowledge by the mouthful until he felt he really could not see its ambiguous fish-like face anymore. And if being full of cake and fed up with knowledge was not enough, Boxy had also acquired level 10 in Tinkering and another prize to go with it, preferably to this address:

1FUneWXoi6eqC2p1F2rDrEsrApawPa9wCx

 

August 16, 2014

Boxy and His Leaky Bottle

Filed under: Eulora — Diana Coman @ 3:18 pm

Leaky Treebark Flask crafted in Eulora by Boxy Boxster.

It looked like a pile of numbered cubes as it bobbed gently and flew over the landscape and under the sea. It was Boxy and it had not a worry in the world nor in his head, for his head had holes shaped like one, two, three… But it had no purpose either and so it was in search of a purpose that he explored the whole land of Eulora – a land that was just learning to be.

It searched the green and stripy plains of this land and it found fruit, but it was rotten. It found cattle, but it was dead and it found fire that had been extinguished. It searched the blue waters of Eulora and it found a ship, but it was wrecked. It found the pearls of molluscs, but they were dull and small and ill-looking. Wherever it looked, it found mainly death and decay in this newborn land.

From his wanderings, Boxy collected the little bits and pieces left behind from what had once been life: a rotten fruit, a piece of bark, a bit of leather, a page from a book. He studied them a long time and he imagined them as something new, something that would be at least of a little use to someone, somewhere, sometimes. Keeping that image of new strong in his mind, Boxy then set to work, to call forth usefulness if not life out of the remains of former beauty and purpose. And he obtained… a Leaky Bottle (for remember, Boxy’s head was leaky itself and he had little to work with in the first place). But leaky as it was and just a bottle as it may be, it was HIS and of his own making, the very first crafted thing Eulora had acquired. For one was Boxy Boxster and he had won the first crafting prize!

Update: …and something to go into that leaky bottle!

cheap_gin

Work on what matters, so you matter too.