So You Want to Make an EOD

A Guide to Custom EODs from Start to Finish

by The Architect

Table of Contents

  0.  Assumptions

  1.  Introductions: Personal and Otherwise

  2.  Getting Started and FSO.Content

  3.  EOD: to the Power of Three

  4.  Volcanic: The All-Powerful “Invoke Plugin” Prim Descriptor

  5.  Locals, Attributes, and Temps, oh My!

  6.  Events Client and SimAntics

  7.  Joinable vs. Non-Joinable EODs

  8.  Ticks, Tips & Tricks about Thread Safety

  9.  The User Interface IS the User Experience

10.  Style, Comments, and Some General Do’s and Don’ts

11.  Other Resources

12. Appendix A: Updates or Changes

0. Assumptions

Object-Oriented Programming (OOP)

Whether you consider yourself a coder or not (I certainly do not) you need a strong grasp of OOP. The EOD aspect of FreeSO is a great place to learn. If you’re new to it, don’t rush yourself. Start by taking a look at some finished EODs, the UIs and the Handlers. If you’re not yet ready to download an IDE, all of the open-source code can be viewed in the repository on GitHub.

EOD UIs: https://github.com/riperiperi/FreeSO/tree/master/TSOClient/tso.client/UI/Panels/EODs
EOD Handlers (server types): https://github.com/riperiperi/FreeSO/tree/master/TSOClient/tso.simantics/NetPlay/EODs/Handlers

Being able to step through and follow someone else’s code is also a skill in and of itself. Obviously my hope is that this guide will significantly cut down on the time you would need to spend doing this. However, I’ll be suggesting you go take a look at certain EODs that are already finished if you want to figure out how something was already done in FreeSO. Much of what I did was cut and paste from what was already there—or at least inspired by it.

Above all, be sure to value process more highly than product. It’s not worth doing if you don’t enjoy it. If you spend hours of your free time trying to make something work and ultimately it doesn’t, at least you gained some experience and grew as a coder and an artist.

 

Software and IDEs

Downloading and installing Visual Studio 2017 (the version recommended at the time this guide is being written) is crucial. If you’re like me and you never used it before, it can be quite daunting. Take it a step at a time and research, experiment, and read. This was my first challenge, and I got better the more I kept at it.

Using GitHub is required for submitting your code to the repository, so GitHub Desktop is my recommendation. That being said, I literally have nightmares where I’m trying to resolve merge issues…Half the battle of writing EODs for FreeSO for me has been figuring out issues when submitting pull requests. I am not the person to offer you any guidance with this.

We are extremely privileged to have a custom FreeSO IDE, fashioned in the spirit of Maxis’ assembly program, Edith, created by FreeSO’s main developer, Rhys Simpson. The program is called Volcanic and it is included in your FreeSO installation.

Volcanic, an IDE made by Rhys Simpson for FreeSO

Volcanic is your one-stop shop for everything Simantics and sprites related. It’s all fine and good if you want to write a C# EOD server handler and UI, but you’ll also need an object to access it. While there is much more on this below, know that this is not a comprehensive volcanic guide so you may need to team up with someone who knows more about it, or consult other guides such as those found in the Other Resources section.

Each object in FreeSO is an .iff file. If the objects exist in the The Sims Online™ assets, they are packed away in FAR archives. For this reason, and in order not to alter these files, any changes made to such an object will generate a .piff file, which will reapply any object changes at compile time. Any new content (CC) added, however, will be added as new .iff files and any changes to them will be made directly to the .iff files, without using a .piff file. The distinction here is important so that you know whether or not to check your \Content\Patch\User\ directory for a .piff file, or just find your .iff object in the \Content\Objects\ directory. The parent directory here would likely be \FreeSO\TSOContent\FSO.IDE\bin\x86\Debug\* but don’t worry, there is more on this below in Getting Started.

 

A Word on Legal Matters

Make sure you understand Freeso’s open source license before you offer any of your work toward it. It is of critical importance that you understand anything you submit must be your own original work. Your use of FreeSO and any submittal of code implies your acceptance of and compliance with the license.

Any assets you use in any custom object or UI must be available in your legally obtained copy of The Sims Online™.

This means that even if you legally own a The Sims™ expansion pack, you cannot submit work that uses assets from it, as players of FreeSO would also be required to legally obtain that expansion pack in order to utilize it.

Of course you may also submit 100% original work that you create to be in the style of The Sims Online™, such as the custom slot machine (with custom slot stops) made by S1ndle pictured below. Note that the lights, buttons, spritefont, and backpanel located on the UI are from the original The Sims Online™ assets.

“X Marks The Spot” Treasure Slots Machine, using original art by S1ndle

We do not distribute or condone the illegal distribution of anything related to The Sims™ franchise for which Electronic Arts, inc. (EA) reserves all legal rights.

Table of Contents

1. Introductions: Personal and Otherwise

Who are you again?

Nobody important.

For our purposes, you only need to know the following:

  • I do not have a programmer’s education or background: I am self taught
  • Before making EODs for FreeSO, I had never seen C# but only worked with Java and Actionscript 3.0
  • For the challenge of learning something new, I wrote these EODs for FreeSO: Slots, Trunk (costumes), Timer, WarGame, GameCompDrawACard, Band, Roulette, Blackjack, HoldemCasinoEOD, and SurpriseEOD

What exactly is an EOD?

Let me just stop you right here and tell you that none of us on the development team at FreeSO have any idea what the acronym EOD stands for—suffice to say it is used to denote a specific client/server protocol that allows special functions outside of the standard engine framework of FreeSO. EODs utilize a special user interface that replaces the “live” panel which allows the player to interact with the object in a unique way.

WarGameEOD

 

VMEODClients and VMEODServers

A VM is a Simantics Virtual Machine (cf. FSO.Simantics.VM) and it’s how every single object in a lot (and I am including all Sims, too) is identified and communicates with the global server. A VM that is designed to connect to and communicate with an EOD server is a VMEODClient. And wouldn’t you know it, a VM designed to serve these clients is called—you guessed it—a VMEODServer.

The EOD host, called VMEODHost (cf. FSO.SimAntics.NetPlay.EODs.VMEODHost) is literally like a server within the lot server. It’s responsible for creating, disposing of, and hosting all VMEODServers as well as controlling the flow of all EOD Events. When a player connects to an object, the object reaches out to the VMEODHost for the appropriate VMEODServer type, called a handler or plugin.

 

VMEODHandlers a.k.a. VMEODPlugins

So earlier when I said I wrote that list of EODs, this is what I actually meant: I wrote unique VMEODHandlers for the VMEODServers to serve to the participating VMEODClients for the sake of idiosyncratic gameplay. The VMEODServers refer to them as handlers, but we also refer to them as plugins, hence the naming convention for each EOD (e.g. VMEODBlackjackPlugin). I also wrote the corresponding user interface (UIEOD) for each plugin, which is the UI that the players see when connected the respective plugin. More on that in the UIEODs section below.

So how do the UI, the plugin, and the in-world (callee) object work together to deliver the EOD? The answer is by using Events.

 

Events

EODs are event driven: when you pull that slot machine lever in your UI, a few series of events take place that cause the plugin to: accept your proposed bet amount, check to make sure you can afford it, deduct the bet from your balance, animate the slot stops in the UI, animate the object in the lot, animate your sim, play sounds, etc. Events are so important that I’ve dedicated an entire section to it below, but for now it’s only important that you understand that all events, including player connection and disconnection, are sent through a VMEODClient—or client for short.

There are two types of events: client events and Simantics events. Client events are used to communicate between VMEODClients and the VMEODServer, technically between the UIEOD and the VMEODServer, so they are always executed from VMEODClients that contain playing character Sims (avatars). Simantics events allow the plugin to communicate with the in-world object that requested the server. Although Simantics events are also executed from VMEODClients, they bubble up through the VMEODServer and VMEODHost and are eventually caught by the Simantics engine. There the event type and its arguments are queued to be sent through the VMInvokePlugin Primitive of the appropriate VMEntity object, e.g. the slot machine on which you’re playing. Don’t worry, as I said, there’s a whole section dedicated to this.

 

EOD Categories: Joinable vs. Non-Joinable Plugins and Job Controllers

There are three categories of EOD plugins in FreeSO: non-joinable plugins, joinable plugins, and job controllers.

Non-joinable plugins are always single-player, however not all single-player objects use non-joinable plugins. Non-joinable objects create new instances of the VMEODServers on demand once a player connects to the object, and these instances are disposed of when that same player disconnects. There is only one user, therefore the VMEODServer only communicates with one VMEODClient. These objects also do not utilize non-playing character (NPC) Sims. Some examples of non-joinable plugins include Slots, Trunk, and Timer. Some examples of single-player objects that actually use joinable plugins include the Nightclub’s Dance Floors and DJ Stations.

Joinable plugins differ from non-joinable plugins in that their server handler instance is not tied to any specific playing character client (or NPC client) that joins the object. This primarily affects how the plugin deals with events. It also allows player Sims to join (and leave) their fellow players on existing VMEODServers at any time. Joinable plugins can make use of NPC Sims and their VMEODClients, as well.

Let’s say for example that if Bob Newbie decides to play some pizza on the PizzaMakerEOD, he might invite three of his friends to start a fresh game, creating a new VMEODServer instance. But if Bob later decides to take a break and let Bella Goth take over his spot, it would be very bad if the VMEODHost decided to dispose of the VMEODServer instance while the other three players are still connected. It would also be bad if the VMEODServer continues to send messages to or through Bob’s VMEODClient after he disconnected from the VMEODServer to let Bella take his place.

There is much more on joinable vs. non-joinable plugins below. Some examples of joinable plugins include PizzaMaker, Blackjack, and WarGame.

Job controllers are plugins that are loaded immediately when a job lot is opened. Currently there is only one, and it’s the VMEODNightclubControllerPlugin. The other two currently available jobs (career tracks) in FreeSO do not require a UI, so they are not handled by the EOD system. The Nightclub Job Controller acts as a parent plugin to the other EOD objects on the lot: the DJ Station and Nightclub Dance Floor. This central point of communication between these different EODs is crucial to the nightclub job as the objects interact with each other (e.g. the dance floor lights up based on the actions of the player using the DJ Station UI) and the synergistic scoring system looks at the performance of both DJ and Dancer. It also sends simultaneous commands to all NPCs and plugins for round phase changes.

 

UIEODs

Honestly your best introduction to the UIEODs would be go to look at the ones that have been completed. Ironically I’m finding that I don’t have a lot of advice to give about Custom UIEODs, because every EOD I wrote (except for HoldemCasinoEOD) was more like putting together existing puzzle pieces from the TSO files and scripts to bring back plugins that we remember from The Sims Online™.

I suppose BlackjackEOD could be considered a custom plugin because, as you’ll see below, my final UI layout did not look like Maxis’ images of it that they released while the TSO was still in alpha.

Blackjack UI concept image from alpha version of The Sims Online™ courtesy of TSOMania

Blackjack UI from FreeSO

You’ve also no doubt noticed that HoldemCasinoEOD very closely resembles BlackjackEOD, both UIEOD design and gameplay style. Since so much of BlackjackEOD was able to be reused, re-purposed, or supplemented in order to make HoldemCasinoEOD work, it still feels slightly inaccurate to consider even this EOD to be true custom content. But the sprites and 3D model were 100% custom made and configured by the FreeSO mod team’s infamous duo, S1ndle and Raeven.

HoldemCasinoEOD: The first 100% truly custom EOD, although it uses many of the assets of BlackjackEOD

Try to keep your UIEODs in the milieu of The Sims Online™. The UI of The Sims™ games has certainly transformed over the last decade, and while you may be partial to a newer iteration, it does no good for FreeSO players if you try to modernize your one EOD to look like a totally different game. Your first goal should be to try to use only existing textures from the TSO installation before adding any custom ones. There are quite a bit of textures from various parts of the game, and there are even quite a few unused ones, too. Use those first.

In order to view all of the textures, you’ll need a FAR archive unpacker. You can download the one in the FreeSO repository. The archives are the .dat files in your TSO installation directory (\The Sims Online\TSOClient\uigraphics\). Select a .dat file to load it in, then extract it somewhere and you’ll see a folder full of bitmap and targa images. Then you can begin browsing all of the UI textures and pick and choose what fits your UIEOD design.

In the user interface section below I walk you through how to find the asset ID for any texture using its filename, as well as putting together the few ubiquitous pieces of the UIEODs that are found across all EODs. I also offer tips regarding design and general user experience and even some suggestions for which other UIEODs to look at for certain design elements that have already been implemented.

Table of Contents

2. Getting Started

Fork, Clone, & Compile

[Much of this section has been redacted until FreeSO is nearing full release.]

That should remove the errors from your error list and allow you to compile and run FreeSO. You’ll notice at the top there are three drop-down menus. You’ll definitely want to use the [Debug] option and [Mixed Platforms] when you run anything.

When testing EODs, you’ll likely want to run both FSO.Windows (or FSO.IDE aka Volcanic) and FSO.Server at the same time. To run both at once, right click “Solution ‘FreeSO’ (36 projects)” on the top of your Solution Explorer and choose Properties. Here you are given the option under Startup Project to choose Multiple startup projects. Just chooses the ones you want and use their respective drop-down menus to choose Start.

 

Intializing the Database and Config.json

[Redacted.]

Now you are ready to test any new code that you add.

 

FSO.Content — How to Use .iff and .piff and Other Miscellaneous Files.

Any changes to you make to an object is a change you’re making to its corresponding .iff file. Objects also can use globals or semiglobals, which also have their own separate .iff files. Globals are a collection of object interactions that are (or can be made) accessible by every object in the game. Salvage is a global interaction, as is repair, so these functions are handled by global .iffs. Many objects are of the same type, so they use many of the same trees. These are the semiglobals. In situations where you want to change the behavior of all costume trunks, for example, it’s better to only have to change a few trees in the semiglobals than to make the same change in 8 separate .iff files for each of the different trunks.

Once you’ve made a change to an .iff file, you’ll see on your main volcanic window that the changes will be listed in the Resources tab. Each sub-entry to an .iff file is a BHAV and it will match the name and #number of the tree that was open and in which you made changes. Sometimes you may wish to cancel a change you made (or you don’t recognize one) so you are able to discard changes by chunks of one or more BHAV. Once you are satisfied with the listed changes for the .iff, be sure to select Save Changes before closing Volcanic.

Save your changes by .iff or discard them by chunk of BHAVs in the Resources tab of Volcanic

Saving changes will do one of two things (as described above in Assumptions) depending on whether or not you made changes to an .iff found in The Sims Online™ files (far archive) or a custom content (CC) object .iff file. If the former, a .piff file will be generated in the \Content\Patch\User\ directory relative to wherever you’re running Volcanic. If the latter, the changes will be applied directly to the .iff file of the object in the \Content\Objects\ directory relative to wherever you’re running Volcanic.

If you’re running Volcanic from Visual Studio (you chose FSO.IDE in your startup), your parent directory will be \FreeSO\TSOContent\FSO.IDE\bin\x86\ and then \Debug\Content\ or \Release\Content\ depending on which you’re building. Otherwise you are likely running it directory from the FreeSO installation directory, so you will find \Content\ in the same location as Volcanic.exe.

Once you locate your .(p)iff file, copy it in Windows and (while still in Windows) navigate to the \Github\FreeSO\TSOClient\tso.content\Content\ directory. If you’ve copied a .piff file, paste it into the \Patch\ folder, and if you copied an .iff file, paste it into the \Objects\ folder. Now back in Visual Studio, rebuild the solution and the next time you run it your .(p)iff file will be copied everywhere it needs to be. Thanks, dotequals!

It’s extremely important that you delete the .piff files from \Patch\User\ if you have copied them into FSO.Content. This is because those files are loaded last, which means you may see changes in Volcanic from them that do not reflect the versions that are copied everywhere else, such as on the server. In order to ensure that the .iff you see in game is the same version on both server and client, you must remove .piff files from \Patch\User\ before making further changes, or your game will continually desync between the mismatched client and server versions, causing unpredictable behavior.

Note: The section below is deprecated. It is no longer the proper method for copying files into FSO.Content. Read the above section or visit this page for more details.

Once you locate your .(p)iff file, copy it in Windows and return to Visual Studio and find the project named FSO.Content in your Solution Explorer. You’ll notice the folder structure here mirrors the directory structure of FreeSO. So now expand \Content\ and right-click Objects and paste your .iff (or right-click Patch and paste your .piff) and you should see it appear.

FSO.Content layout mirrors the installation directory structure of FreeSO

This is also the place to paste other types of files, such as 3D re-meshed sprite replacements (.fsoms and their textures), custom textures for EODs, custom lot blueprints, etc. Clicking on files here opens a properties menu that offers copying options. (Note that the iOS folder here is no longer used, as per our respect to a request from Electronic Arts, inc. to not port FreeSO to mobile devices.)

Select a file to alter its build properties

For everything EOD related, you will want to select the Content Build Action (the default is none) and Copy if newer (default is Do not copy) to ensure that the object will be synchronized across the server, the client (FSO.Client), and Volcanic (FSO.IDE). If you are copying a file that is already present in Visual Studio (because you’re copying a newer version) you will be prompted to overwrite the existing one and you won’t have to select the build and copy action—it will be remembered. I really recommend Cleaning the Solution after .piff and .iff overwrites before running again, due to unresolved inconsistency issues with .iffs that even other developers than just me have experienced.

 

Committing Changes and Pull Requests

As I mentioned in Assumptions, I frequently struggle with merge conflicts with GitHub. It’s quite maddening actually…But the best advice I have is to always make a backup of your classes and .(p)iffs before committing or making pull requests. Several times the merge conflicts were so intense for me that I found it easier to delete my repository and re-fork at the proper version before adding my work back in and committing. This is definitely not what I recommend, and learning how to navigate merge conflicts with a merge tool is the proper way to go. But making backups will only help you, so do it. Trust me.

At the time of the writing of this guide, the workflow of FreeSO is to make a pull request with 100% working code. For EODs, this means:

  1. A new server handler class, e.g. VMEODCustomContentPlugin.cs in FSO.SimAntics
  2. A new entry for this new handler type in VMEODServer.cs (more on this in the Power of Three section below)
  3. A new UI class, e.g. UICustomContentEOD.cs in FSO.Client
  4. A new entry for this new UI in UIEODController.cs (more on this in The UI section below)
  5. A new .iff pasted in the \Objects\ directory of FSO.Content (or .piff in \Patch\) for updates to the object using the EOD.
  6. (Optional) Any peripheral or utility classes (e.g. AbstractPlayingCard.cs for VMEODBlackjackPlugin)

It is likely that numbers 1 through 4 (and optional 6) will always be required in order to add an EOD to FreeSO. However, number 5 is likely to change in future updates with the way the server can be updated to accommodate custom content.

Regardless, check the Appendix A section below before attempting to submit a pull request as the method for adding content to FreeSO may change as it is updated.

Table of Contents

3. EOD: to the Power of Three

It’s best to think of an EOD as a relationship between three entities: the server handler, the user interface, and the in-world (callee) object. All three work in tandem to deliver the EOD content. All three are equally necessary and have specific unique functions.

The server handler will be your nerve center, and since it’s located on the remote server, it should be treated as the source of all data as it will be the most secure and reliable. It will be the place that validates all steps of the process before continuing. It can communicate with the in-world object through the use of SimAntics Events, and it can communicate with all user interfaces through the use of Client Events.

The user interface (UIEOD) is inextricable with the user and with the user’s installed client. Never trust a client, and never trust a user. All data received from a client must be validated as legal by the server. FreeSO is an open source project, so everyone can take a look under the hood and see how it works. It would be easy for a user to modify their client to try to bet $1,000,000 on a single slot machine pull. The VMEODSlotsPlugin.cs server handler needs to know what the maximum allowed bet is on any particular slot machine type.

The in-world object (.iff) will handle everything in world, with very few exceptions. It can use any Primitive, a SimAntics function, to make things happen in the lot. You will also want the object to be consistent with how the user experiences the UI. If you are dealing cards to the players of BlackjackEOD, then you’ll want your NPC dealer to actually use the “deal cards” animation. Even if Sims are just considering which costume to put on, a nice repeated idle animation of them searching a trunk or thinking will help to add to the immersion. You get to be especially creative here!

Relationship chart of the three aspects of an EOD: server handler, UIEOD, in-world object (.iff)

Writing an EOD is a lot of work. Perhaps you were of the mind that we were simply “fixing broken objects from TSO” to bring back our favorite EODs like pizza and band? But as you can see, the breadth and complexity of the work required expands further than simply writing some simple C# classes. Even though the “existing” TSO EODs had a few script files that would generate some assets and put them somewhere in the UIEOD, making them function and writing a whole server handler is always written from scratch. Thankfully for these “existing” EODs, the in-world objects had existing trees (a.k.a. BHAVs) that could be followed to help provide insight into what the object’s function was and how to utilize it in the world. A brief look into the “Interaction: Be Timekeeper” tree of the timer object revealed exactly what was expected of the TimerEOD server handler—start the timer, stop the timer, change its mode, be idle, and set the timer. (Note that I moved nodes around in this volcanic screenshot so the arrows didn’t cover the data so it could be more easily read.)

The “Interaction: Be Timerkeeper” tree of the timer.iff object: a look at the possible events

I really break this down in both the next three sections, as well as in the Events section below, but those white boxes you see that say Private are called subroutines. Double-clicking on them opens their corresponding BHAV/tree, which is also listed in the “trees” menu of the object in volcanic. They are “private” because they are only used in the local object, in this case timer.iff, as opposed to globally (by all objects) or semi-globally (by a specific group of objects, such as all costume trunks).

Of course for your custom EODs, none of this will exist until you write it from scratch. This is merely a window into my process, having had no guide such as the one you’re reading now. I like to tell people that it was like assembling a puzzle with missing pieces, and I was required to fill in the gaps with my own creativity. There is (to our knowledge at the time of writing this guide) no surviving video footage of how slot machines worked, for example, so even though I could easily see what was meant to happen when the user pulled the slot handle, the animations in the UIEOD were completely by my design without anything after which to model them.

 

VMEODCustomPlugin.cs: The Bare Bones

Here is where to put your EOD class files and how to register them. Create your new EOD (following the naming convention) in FSO.Simantics in the directory you see in the screenshot below. You can right click the folder, add, and choose class.

Adding a new handler (plugin) class next to the others

Now you need to register that handler in the VMEODServer class. You will find this class in the parent directory where you found \Handlers\.

Find VMEODServer.cs in order to register your new handler

In order to register your new EOD you need to select an 8-digit hexadecimal number, which will be the GUID (globally unique identifier) of your EOD. This GUID will be the key to finding your server handler and its UIEOD, so make a note of it. It will be saved as an unsigned integer with a preceding “0x” making it ten digits long. You can use any hex number that isn’t already listed.

Register your handler class

You should probably enter your key under the FreeSO specific comment since your custom EOD did not originate in The Sims Online™. List your GUID and the name of your handler class and you’re all set.

Your best guide to making this class is to read the handler classes that have already been completed. To make it work, just take a look at the few methods you can override:

  • OnConnection: This is called whenever any client (whether a player, a NPC, or the controller) connects to the EOD server. This is the place to check the TempRegisters that were copied from the connection event and to learn more about the connecting client and its avatar, if it has an avatar.
  • OnDisconnection: This is called whenever any client, player or NPC, disconnects from the EOD server. This is an optional method that you can use to configure or clean up some items in the plugin of the joinable EOD.
  • Tick: See Ticks, Tips & Tricks about Thread Safety
  •  

    UICustomEOD.cs: The Briefest Look

    You also need to create a UIEOD class in this directory of FSO.Client.

    Add a new UIEOD class

    Register this UIEOD in UIEODController.cs using the same GUID that you chose for the plugin above. I recommend adding it under the “new for freeso” comment.

    Register your new UIEOD so it is called when the user connects to the plugin of the same GUID

    Note: Some of the great many relics left behind by ddfczm include the VMBasicEOD.cs and UIBasicEOD.cs. I strongly recommend taking a took at them. They can be extended by your new EOD classes as they contain the required methods to make the EODs function, and they can simply be overridden in your classes to suit your needs. At the very least, take a look at them to see what the absolute bare minimum is needed in these classes to connect to an object and see some kind of UI.

    Once declared, the UIEOD will automatically be created when the server handler instance is created. However, to switch the “Live” panel over to the EOD panel you will have to use an event. I break down the steps of putting together a UIEOD in the The User Interface IS the User Experience section below.

     

    Custom_Object.iff: Your Custom In-World .iff

    As I said in Assumptions, this is not a guide suitable for explaining the complex workings of Volcanic, the IDE you will need in order to create and edit object .iff files. This is both because it is so involved that it deserves its own guide, and because I personally know so very little about it. I will only cover what I was able to learn on my own in order to make EODs function. You will undoubtedly need a supplemental guide to perfect your custom objects.

    In fact, neither objects for BlackjackEOD or HoldemCasinoEOD would have any aesthetic function whatsoever if it weren’t for the great Raeven of woobsha.com. She literally transformed what were just empty object husks that resembled furniture (and allowed users to connect to an EOD) into fully immersive, believable in-world objects. Other than Volcanic’s creator himself, Rhys Simpson, Raeven is your best resource for all things Volcanic & object sprite related. Credit for my .iffs for BlackjackEOD and HoldemCasinoEOD also equally go to S1ndle, who made the beautiful 3D meshes and graphics.

    If you haven’t already, join the official FreeSO discord so you can be connected to the people that can help you should you need it. You may also find some assistance in the FreeSO forums.

    And of course, as I’ll say elsewhere, check the Other Resources section below for guides on the many different stages of creating EODs. There are some extremely talented people who have produced excellent guides that I have linked for you there.

    It’s finally time to dig in. The next three sections will primarily cover the server handler (plugin) class and the object, so if you’re looking for more on the UIEOD, jump to its section below.

    Table of Contents

    4. Volcanic: The All-Powerful “Invoke Plugin” Prim Descriptor

    In case you haven’t noticed, there’s a whole lot to learn in Volcanic. Despite being a work in progress, it boasts a vast array of very simple but very powerful options for creating, customizing, and implementing objects into FreeSO. These objects are contained in .iff files. The items that came with your The Sims Online™ installation are never edited directly, but instead are “patched” at run time using .piff files that Volcanic automatically generates when you save any changes to these .iffs. Any custom content objects added to FreeSO, however, will retain all changes made directly in the .iff file. Be sure to read the section about FSO.Content in Getting Started above so you understand .piff vs. .iff files and where to find them.

     

    Some Volcanic-specific lingo

    There a several terms I use, sometimes interchangeably, so I would like to list them here to avoid future confusion (plus I might get tried of using parenthesis):

    • Trees: The Volcanic-specific term for Routines. If you’re editing a tree, you’re editing a Routine.
    • Routines: A collection of Primitives that are executed within any object, one after another, often returning GOTO_TRUE (green tick/check), GOTO_FALSE (red x), or DONE (blue tick/check). There are a few more return types, but you don’t need to worry about them for this guide.
    • Sub-routine: A Routine run inside of another Routine. Their Primitive Descriptors say “Private:” and then the name of the Routine.
    • Primitives: Special types of SimAntics instructions determined by the opcode number. For more information, check out this SimAntics Wiki entry. These are called Prim for short.
    • Primitive Descriptor: A graphic element in Volcanic that looks like a colored box. Clicking on the box opens options on the left side bar for configuring the data for the Primitive. Unsupported or not yet implemented Primitives will display “Unknown Operand” when clicked.
    • Interaction: These are the options available to a Sim when you click any object. An Interaction is technically made up of two trees: a Check Tree and an Action Tree. In order to set an interaction for an object, go to the Tree Tables option in the drop-down menu and click the listed tree table that is purple in color—this means that it is the active tree table, in case you see more than one.
      The interactions listed in the tree table of gamecompdrawacard.iff

      Here you can add or remove interactions, set new check or actions trees, and give the interaction a name that will be seen in the pie menu when the user clicks the object. You can also set many option flags including permissions required for users to be able to see the interaction, such as roommates only.

      Often when I speak of some kind of prim or Sub-routine occurring inside an interaction, I am referring to the Action Tree that is part of the Interaction.

    • Check Tree: A special Routine that is executed very often (especially when a user hovers their mouse cursor over an object) that must return true in order for the user to even see the Interaction that it’s a part of listed in the pie menu. For example, a user will not see the “Play” interaction on the roulette table if they cannot afford the minimum bet required, and the Check Tree is the place that checks their Simoleon balance against the minimum balance set in the table. They also won’t see the “Sleep” interaction on a bed if it is already in use by another Sim.
    • Action Tree: The Routine executed first during an Interaction provided the Check Tree returns true. If using the “Push Interaction” prim, once again this is the Routine that is executed first when the Sim reaches the Interaction in their queue.
    • Smart Tile vs. Dumb Tile: A multi-tile object in SimAntics is an object made up of many objects that each have their own tile. The need for many tiles is usually for graphical/aesthetic reasons, but each sub-object may have their own functions as well. Every object in the multi-object has their own instances of the globally declared attributes. While every object is also able to have a set “Main” tree, which is executed once a lot fully opens and initializes, there is usually one tile that you want to be responsible for special functions like room scoring and EOD handling. This tile is affectionately named the Smart Tile while the other objects are the Dumb Tile(s). For EODs, you will want your Smart Tile to also be your Lead Tile. For more on the lead object and lead tile, check the Attributes section of Locals, Attributes, and Temps, oh My!
      Blackjack Object: A Smart Tile’s (left) “Main” tree, versus a Dumb Tile’s (right) “Main” tree

     

    The Invoke Plugin Prim Descriptor

    In the previous section you created your two class files, one for the server handler (plugin) and one for the user interface (UIEOD). But you need your in-world (callee) object to request your plugin class and your UI class. As I mentioned in several places already, this guide assumes you already have your .iff object ready for to set up with an EOD. Let’s look at whichever interaction (tree) is responsible for calling the plugin.

    The Invoke Plugin prim descriptor in the slotmachine.iff tree “play”

    On the right side of the screenshot of slotmachine.iff (“play” interaction) above, the red box that you see is the graphical representation of the Invoke Plugin primitive (cf. FSO.SimAntics.Primitives.VMInvokePlugin), which is called the Invoke Plugin Descriptor. When you click on this, you will see customizable fields appear on your left side bar.

    The hexadecimal number on top of the red Invoke Plugin descriptor is the plugin ID, and it should match the GUID you chose earlier for your classes in the previous step. You may have noticed in the screenshot, however, that the field to input your plugin ID and its output on the red box do not match. That is because at the time of the writing of this guide, the input number is a decimal representation of the hexadecimal GUID. A simple hexadecimal to decimal calculator will be your quickest and easiest method to input your proper EOD GUID. (Be sure to check Appendix A to see if this or other syntax has changed).

    The Avatar, Object, and Event fields allow you to set which local variable holds the value for the avatar (although sometimes object) connecting to the EOD (the caller), the object connecting to the EOD (the callee), and the current event in the stack of events, respectively. I will cover more on this, as well as sending & receiving arguments (as temp0, temp1, etc.) with an EOD, in the next section.

    “That’s all fine and good for an existing object, Architect, but where do I put this in my custom object?” Fair question, reader. Whether or not you’re making a joinable or non-joinable EOD is really what dictates where your Invoke Plugin prim belongs. While a seemingly deceptively simple setting, joinable vs. non-joinable EODs are very different from each other.

    While this isn’t 100% true, a couple of good rules of thumb for knowing whether or not to make your EOD joinable or not are:

    • If your object uses zero NPCs, allows only one player/user, and does not need to communicate with any other EODs, it’s non-joinable.
    • …otherwise it’s very likely joinable. If you use any NPCs or allow more than one player, that is a dead giveaway that it is joinable.

    Non-joinable EODs only need one single instance of the Invoke Plugin prim, and it belongs in the interaction where you want the user to connect to the EOD—whether it’s “Play” for the slot machine, “Be Timekeeper” for the Timer, or “Browse” any costume trunk, etc. When the user initiates this interaction, they also connect to the EOD server. When they finish with the EOD and disconnect from it, the interaction will finish in their queue, allowing them to finally run to the toilet or whatever is their next queued interaction. Obviously if your object uses multiple interactions to connect to an EOD for different functions, then you need an instance of the Invoke Plugin prim in each of the separate interaction trees. A good example of this is the slot machine’s “Play” versus “Manage” interactions.

    Slotmachine.iff “Play” and “Manage” interaction trees both need an instance of the Invoke Plugin prim

    The interactions are separate because they need to send different data to the plugin as arguments and they need to handle events differently. In this case, it is certainly possible to have a single interaction check to see if the connecting Sim is the owner of the object and modify the arguments to send to the EOD server accordingly, thus eliminating the need for a second interaction. However, this would prevent the machine owner from being able to play on their own machine, which is not true to the original behavior of The Sims Online™ slot machines. The decision to use more than one interaction depends on your design choices, but the object only needs needs one interaction (and therefore one Invoke Plugin prim) for player Sim connection, disconnection, and EOD event handling.

    Joinable EODs are more complicated, and will require at least two (but probably three or more) instances of the Invoke Plugin prim:

    • In a player interaction for connecting
    • In a player interaction for disconnecting
    • In a sub-routine (a separate self-contained tree that is not set as an interaction, often found inside interactions) for checking for new events
    • (Optional) In a NPC interaction for connecting
    • (Optional) In a NPC interaction for disconnecting
    • (Optional) In a sub-routine for “callback” event invoking
    Blackjacksemiglobal.iff uses two separate sub-routines for events: “Poll Plugin” to listen for events initiated in the server handler and “Send Callback Event” to send events back to be received in the server handler

    BlackjackEOD is a good example of a joinable EOD. The table must be opened, which allows an NPC to connect to the plugin, and then up to four players can connect (or disconnect) at any time. It uses separate interactions for players to start and finish playing blackjack, separate interactions for the NPC to start and finish being the dealer, and two sub-routines to handle SimAntics events. Each of the trees, whether interactions or sub-routines, use an Invoke Plugin prim.

    Finally, you will notice the GOTO_TRUE and GOTO_FALSE arrows that extend from the Invoke Plugin prim. Unlike the blue arrow (GOTO_NEXT), you need to know when the prim will return true and when it will return false. (Hopefully) you also probably know by now that GOTO_TRUE and GOTO_FALSE are handled differently by each prim. An Expression prim is simple: if the statement is true (e.g. 2+2==4), the prim returns true; otherwise it returns false. However with Invoke Plugin, the prim returns true when the server handler closes (or when no server is detected in the first place) and false when there is a SimAntics event that needs to be evaluated.

    Table of Contents

    5. Locals, Attributes, and Temps, oh My!

    Now that you’ve created a plugin with a GUID, there’s loads of information you can (and in most cases, have to) send to it. Arguments that you send to the plugin are known as Primitive Operands, which are handled differently by each different primitive. For Invoke Plugin, you can send:

    • Avatar: (sometimes optional) The local variable (or local) that contains the objectID of the avatar interacting with the EODServer
    • Object: The local variable that contains the objectID of the object for which there needs to be an EODServer
    • Event: The local variable that contains (or will contain) the top event in the event stack
    • Joinable: A flag declaring true if the EOD is joinable, false if not

    You send the first three arguments by declaring which local variable in your tree/routine contains the data you want the plugin to read. The joinable argument is a simple Boolean, which relies on the tick box on the prim descriptor.

    In addition to these operands, you can also send up to 20 parameters, known as Temps or Temp Registers to the plugin every time you use the Invoke Plugin primitive. It’s as simple as initializing temp0, temp1 … temp19 just before the Invoke Plugin prim. More on those later, though.

    Locals and Temps have to be initialized in each routine, with locals needing to be declared with every unique routine/tree. Some routines don’t use locals at all, so none will be listed. But if it’s consistent data you want, look no further than Attributes. Attributes are initialized in the object’s init tree, but are later properly assigned their variables from the VM as the lot restores from its saved state from the server. Attributes can be accessed from any trees on the same object, but beware that multi-tile objects have different attributes per object.

    Below is a closer examination of each of these different Volcanic-specific data types and their roles in the Invoke Plugin primitive.

     

    Locals

    Every time the Invoke Plugin prim is utilized, its VMStackFrame (cf. FSO.SimAntics.Engine.VMStackFrame.cs) is compiled and accessed in order to appropriately execute the routine. The VMStackFrame class acts as a sort of “dossier” that highlights the important information, pared down from the immense amount of data found in the VMContext (cf. FSO.SimAntics.VMContext.cs) and the VMThread (cf. FSO.SimAntics.Engine.VMThread.cs) of the lot.

    Each time this “dossier” is compiled, every single local variable from the object’s routine is copied and made accessible by the game engine. By declaring which local variable contains the information we need for Invoke Plugin, the primitive can fetch the information quickly from the VMStackFrame in order to complete the routine of dealing with the EODServer—whether creating, disposing of, or handling events.

    On the right you can see the Local Variables or “Locals” of the selected tree/routine

    Once you have selected the tree that you wish to edit, you can add locals and name them whatever you please. Once they are declared here, you can assign them any short (a number from -32,768 to 32,767) when you edit the tree.

    Note: At the time of the writing of this guide, an issue exists with Volcanic that does not allow you to rename Locals that you create yourself in Volcanic. Even after saving changes, the names will revert to the default names (local0, local1, local2, etc.). However, you are able to use these locals with the default names. You are also able to rename any existing locals.

    Leaving the Locals with their default names is acceptable, though not ideal

    Once you’ve chosen your prim (e.g. Expression) you can find the locals listed on the left.

    Placing a prim instance and selecting a Local for the Left Hand Side (LHS) variable

    Now that you have declared and initialized your locals, you can use the Invoke Plugin prim descriptor to specify which locals contain the data to be sent to the EOD server. When you click the red Invoke Plugin box, your options become available on the left sidebar. Here is a routine from dressersemiglobal.iff where we can see the locals sent to the server each time an avatar uses the “Change Clothes” interaction.

    Specify which locals contain the data to send to the EOD server: dressersemiglobal.iff example

    The local containing the avatar’s objectid is local4, which is aptly labeled “personID”. You can see personID being assigned My object id—the objectid of the calling object—which is the avatar connected to the EOD. Similarly, local5 is labeled “objectID” and it assigned the Stack Object ID, which is the objectid of the callee object, to which the EOD server belongs.

    Finally, the local3 called “eventID” is assigned a value of -2, which is the universal event value for “connect to EOD” but this will be covered in depth in Events.

    So in layman’s terms, this routine is telling the SimAntics engine to connect this player avatar to the VMEODDresserPlugin using this specific dresser object. Since this is not a joinable EOD, an instance of the Dresser Plugin will be created for this player.

     

    Attributes

    In addition to Locals, Temps, and Parameters, you’ll notice many sub-selections under My and again under Stack Object:

    • The data found under My are for the caller object, which will either be the object on which this routine is being run, or the avatar of the connected Sim—usually player, but sometimes NPC. If the tree is being executed in an interaction, then these data will refer to the Sim who is using the interaction (even NPCs), so you’ll want to use the Person Data sub-menu. Otherwise the My is referencing the object on which the routine is being run, so you’ll want to use the Attributes of the object. It can be very confusing because no matter which is true, Volcanic will always list the attributes of the object in the menu, even if the My actually refers to an avatar.
    • The data found under Stack Object are for just that—the stack object. While the object referenced by the Stack Object variable changes often, at the start of any routine the variable will always reference your callee object. This is the object for which you are creating an EOD server. If your object is a multi-tile object (e.g. Band, Pizza, Code), the Stack Object by default will be the lead tile.
    Note: At the time of the writing of this guide, the only way to determine which tile is the lead tile is to check which ObjectID is listed as the Multitile Lead under Volcanic’s VMEntity Inspector tab. Here you can see that the Blackjack Table Object’s lead object is NOT the “A” object, so it must be the “B” object. There is also currently no way to change the lead object at this time.
     
    Note: The method in the caption above will only work if the separate tiles (separate objects of a multi-tile object) actually have different names. If they do not—as you see in the second screenshot where all three Roulette objects have the same name—you will have to set a breakpoint in Visual Studio and do some serious investigating of each object to figure out which is the lead and how you can determine that looking back in volcanic. This another reminder to check the Appendix A section below.

    Probably the most helpful piece of data you will find from the My…Person Data… menu is objectID which you will want to send to the Invoke Plugin prim in a local. However, as you can see, there is quite a wealth of information accessible using this method with regard to the avatar. This is all also accessible in the EOD handler class so you may prefer to access it there. A good example of this is the VMEODBandPlugin class, where I wanted to access the skill level of a certain skill based on the instrument that the Sim chose to play.

    Accessing the level of a Sim’s creativity skill in Volcanic
    Accessing the level of a Sim’s body, charisma, or creativity skill in the handler class

    I found it much cleaner to access the skill level information in the handler class instead of sending through the Invoke Plugin prim. Nearly all of the data you might need for your EOD can be accessed from within the handler class, but you may need to send those data to the handler in order to properly instantiate the type of EOD you want. How does the costume trunk plugin know which type of costume trunk has been opened, for example?

    In fact every trunk object uses the same semiglobals, trunkglobals.iff, and the same “Change Outfit” Interaction to allow the player to choose from the costumes contained within. How can we know which costume collection to display to the user?

    The sports costume trunk is initialized with a specific number in its aTrunkType attribute, which can be sent to the Invoke Plugin primitive using a temp variable

    Each unique trunk .iff has a different short (number) assigned to its aTrunkType Attribute. We can send this number to our plugin handler class through the Invoke Plugin prim, thus allowing us to curate the collection from which the player can choose their costume. To send this data we will use temporary variables, or temps.

    A enumeration of the different numbers for aTrunkType, with names corresponding to the type of trunk to which they belong

     

    Temps

    Every time the Invoke Plugin prim is executed, the temporary variables ranging from temp0 to temp19 are copied over and are accessible from the thread using the invoker of the connecting client as a reference (cf. FSO.SimAntics.Engine.VMThread.cs). This allows you to send 20 different arguments of short data type to the handler class. Simply initialize your temp variables before the prim in any order and at any time.

    The OnConnection Method of VMEODTrunkPlugin where the temporary variables from the trunk object are accessed using VMEODClient.Invoker.Thread.TempRegisters

    Beware, though, that once you are on the false branch of the prim (and remember this means that there exists an event that needs to be evaluated) the temps may have changed! With each SimAntics event that you initiate from the handler class, you can send arguments of short data type with it. These are copied starting from temp0 and will overwrite any previous data that you may have had in those temporary variables.

    A look at slotmachine.iff tree “Play” shows how temp0 changes constantly—any time a SimAntics event occurs, the value can change

    Finally, temps also have a special function in Volcanic where they can be sent as arguments to any sub-routine executed in your tree. When you select a white sub-routine prim descriptor, you’ll notice that the left sidebar has 4 spaces for input. You can type in any literal number (but don’t exceed the short data type capacity of 32,767) except that when you enter negative one (-1) you will send whatever value is in the corresponding temp variable from temp0 to temp3 into parameter0 to parameter3.

    The order of temps corresponds to the order of parameters—even if you skip a parameter or use a literal, you will always be sending temp0 through temp3

    This allows you to dynamically send data to your sub-routines as arguments, but you must make sure you’ve declared the parameters in the list for your target sub-routine first. If you do not have the parameters declared for that sub-routine in order to accept arguments, they will simply be copied over to the target sub-routine’s temp 0-temp 3.

    A look at sending temps and literals as arguments to a sub-routine: blackjacksemiglobal.iff tree “Poll Plugin” sends temp0 (-1), containing the ObjectID value of the target player, a literal interaction number (13) to the tree called “Push – On Player – Interaction” before setting that player as the stack object in order to display the amount of money that they won over their head. The winnings money value was sent as an argument with the SimAntics event (6) as temp1 and the ObjectID of the player was sent as temp0

    Table of Contents

    6. Events: Client and SimAntics

    Events are the most crucial form of communication between the handler and each player, and between the object and the handler. Client Events allow the handler class to communicate with each user’s interface (UIEOD) and they are the same for every EOD. There are two types of client events based on the type of data sent as arguments: Plaintext (strings) and Binary. SimAntics Events allow the object to communicate with the handler class, and while there is only one type of (and syntax for) them, there is a slight difference in their execution for EODs that are joinable versus those that are not.

     

    Client Events

    The syntax for client events a simple two step process: first create and ascribe a method to handle the event (I like to call an event listener or event handler), then set when your event will execute with its optional arguments. Client events are named with custom strings, so as to delineate between different event types. When an event is triggered from a client’s UI (UIEOD), for example if your user clicks a button on the panel, a listener in the handler class must be present in order to deal with the event. Conversely if an event begins in the handler class, such as a new round starts for betting in roulette, a listener must be must be present in the UIEOD in order to respond from within the client. Check out the functions (called Send) that are responsible for sending the Client Events here (FSO.SimAntics.NetPlay.EODs.VMEODServer.cs).

    Syntax for defining an event handling function, aka event handler or event listener (Note: This is the same for UIEOD and plugin/handler classes)

    If your event sends sends binary arguments, use the keyword BinaryHandlers[], and if it uses strings use PlaintextHandlers[]. Inside the brackets you put the name of your event as a string (make sure it matches exactly wherever you try to invoke the event), and after the equals sign put the name of the method which will handle the event and read its arguments. Be sure that these handlers are defined in your classes well before any client can connect to them. I tend to definite them in the constructor or an init method run immediately.

    Syntax for a method handling a Client Event within the handler class that uses binary arguments

    The functions themselves should not return data and should have the parameters of the event name (string) and the arguments type (byte[] array or string). The only difference between defining a handler in the plugin versus the client is that in the plugin you have a third parameter which will contain the client from whom the event has been sent.

    Syntax for a method handling a Client Event within the client itself that uses a string argument

    For best practices and lowest data usage, try to always use the binary client events. If the arguments you need to send are strings, you can serialize them into a binary array using the static functions available in FSO.SimAntics.NetPlay.EODs.Handlers.Data.VMEODGameCompDrawACardData.cs. You can also deserialize byte arrays here, too. For examples of this, take a look at FSO.SimAntics.NetPlay.EODs.Handlers.VMEODGameCompDrawACardPlugin.cs.

    Executing the event uses a simple method called Send with the first parameter being the (string) name of the event and the second being your single string or binary array argument(s). From within a client (UIEOD) you simply write Send(“event_name”, “argument”). If you wish to send an event to a particular client from the server (handler class), simply reference the VMEODClient and invoke its send: targetClient.Send(“event_name”, new byte[] {1, 255}).

    In UITrunkEOD.cs, when the user clicks the accept button, an event is sent with a string argument containing the outfit ID that the user selected

     

    SimAntics Events

    SimAntics events allow the handler class (plugin) to communicate with the callee object. Events can be executed from either, and received in either. The handling of SimAntics events in the plugin is not too dissimilar to handling client events: you still must create and ascribe a handler method in the plugin, and you use similar syntax to do so.

    The major differences between these events are:

    • SimAntics events do not use string names but rather short numbers (from -32787 to 32787 inclusive) to differentiate one from another.
    • SimAntics arguments are also short numbers, and you can send multiple or an array of them.
    • SimAntics events originating from the plugin are sent through VMEODClients—just like client events—but any particular player’s client cannot be counted on to stay connected to joinable EODs, so an alternative method must be used.

     

    SimAntics Events in the Plugin Class

    An enumerated collection of SimAntics events used by Roulette EOD

    For best practices, create an enumerated list of events, and use the names to help describe the purpose of the event. Remember that negative two (-2) and negative one (-1) are for client connections and disconnections. Do not use zero (0) so as to allow it to be used for idle events (more on that in the next section).

    Trunk EOD: While a simple short would suffice to dispatch a SimAntics event, casting an enumeration constant helps to demonstrate to viewers of the class code what the event is intended to do

     

    When executing the event, simply cast the enumeration constant as a short. You can send from zero to four shorts, including in a short array. Use the SendOBJEvent(cf. FSO.SimAntics.NetPlay.EODs.VMEODServer.cs and FSO.SimAntics.NetPlay.EODs.Model.VMEODEvent.cs) method available through your referenced client.

    Blackjack EOD: SimAntics event listeners and their handling functions are set for four different events

    Specifying a handler method for the event requires casting the enumeration constant into a short, and the syntax for the listener is very similar to client events. This time we use the keyword SimanticsHandlers[] and inside the brackets goes the (short) cast and the name you gave the event. The name of the method follows the equal sign, just like in client events. Your handler method needs two parameters: one for the number (short) of the SimAntics event, and one for the VMEODClient through which the event was sent.

    Blackjack EOD: This SimAntics handler method simply needs parameters for the SimAntics event number (short) and the client through which the event was sent—for joinable EODs this will be the Controller; for non-joinable this will be the player’s client

    In non-joinable EODs, there will only be one client: that of the player who connected in the first place. Obviously this is the client you use to send the SimAntics event. This is because if this client were to disconnect at any time, the instance of the EOD would be disposed. But in a joinable EOD, just because a client disconnects doesn’t mean the EOD should be disposed. We can’t risk an event not being received by the callee (in world object) because the client who was responsible for delivering it disconnects at the perfect time to interfere. That is why we use what has been affectionately named the Controller Client to send events in joinable EODs.

    Roulette EOD: A SimAntics event is dispatched through the controller client in order to set the new maximum bet attribute in the callee object. Then a client event is dispatched to the client of the object owner—the player who is currently managing his roulette table—to signify the successful maximum bet change

    To wrap it up with a technicality: when you “send” a SimAntics event, it is being pushed onto the event stack and will be the first event processed upon the next Tick (as opposed to immediately). It’s important to remember this idiosyncrasy, as it is handled differently in joinable and non-joinable EODs.

     

    SimAntics Events in the Callee: Volcanic

    It is possible to receive and send events in Volcanic, just like in your handler class. These occur in the same trees where you use the Invoke Plugin prim descriptor. As you saw at the end of the Invoke Plugin section, when there exists an event to be handled the Invoke Plugin prim will return GOTO_FALSE and follow the false branch. This is where you want to evaluate your event number, by using an Expression prim to check its value against a literal number. The Local that you chose to define as your event variable will always have the top event of the event stack in it each time you start to follow the false branch. Therefore if you assign any number to that same local variable you are overwriting the top-most event value, and if you point it back to the Invoke Plugin prim, it can handled again back through the false branch. Not only that, but it is received in the plugin class as well, and can be handled using a SimAntics listener. This is how you “send” events from the object to the server handler.

    The “play” tree of slotmachine.iff keeps the player connected to the object by continually overwriting the event value with 0 until it is overwritten in the handler class with something that can be evaluated: in this case (4), (8), (9), or (5)

    In slotmachine.iff “play” tree, if the event being evaluated doesn’t equal four (4), eight (8), nine (9), or five (5), the event value is overwritten by a the Expression prim and sent back to the Invoke Plugin prim to be evaluated. If the connected Sim needs to disconnect due to low motives, the event value is changed to negative one (-1) and the object will disconnect the client via the prim (you don’t have to instruct the object to do so here for a disconnect event). If the connected Sim does not need to disconnect, the event is overwritten with a 0, creating an infinite loop thus keeping the Sim connected to the slot machine until an event of a different value needs to be evaluated. The sub-routine “Global: Idle” which lasts for one Tick will keep the object from throwing a stack overflow error as it will limit the checking of a new event to one per tick.

    SlotsEOD: an enumerated list of the events used

    This is how objects with non-joinable EODs keep the Sims connected to themselves (and the EOD). If the “play” interaction tree was allowed to complete and return true or false, the interaction would finish on the Sim and vanish from the queue, disconnecting them from the EOD. This approach is not possible for joinable EODs, but the appropriate method to handle events and keeping Sims connected is detailed in the Joinable vs. Non-joinable EODs section.

    Both types of EODs, however, can also use this reassignment of event values in order to send a specific event number back to the plugin—events I like to call Callback Events. The “play” tree from slotmachine.iff assigns an event value of six (6) to let the plugin know that the animations of both Sim and object are finished and a new game can begin again.

    In the slotmachine.iff “play” tree, whether the player has won or lost, an event of value six (6) is assigned and sent back to the Invoke Plugin prim so the plugin class knows that the next round can begin and the player can pull the handle again

    BlackjackEOD uses a great deal of call events due to the symbiotic nature of the game of Blackjack. The dealer hands out cards and waits for the player to decide to hit or stand, the player motions to hit, the dealer gives another card, etc. Much of the flow of the game depends on the interactions of the players and the dealer, so I employed Callback Events to signify when the Sims’ animations had completed. This was an aesthetic choice to keep the flow of the EOD similar to a real game of blackjack.

    BlackjackEOD uses many callback events to control the flow of the game

    Table of Contents

    7. Joinable vs. Non-Joinable EODs

    If you’ve been reading this guide in sequential order, then surely you’ve heard me mention joinable vs non-joinable EODs many times already. You may also find this to be review, but since it is crucial that you decide which type your EOD will be, here is the short list from above on how to decide if you should be using a joinable or non-joinable EOD:

    • If your object uses zero NPCs, allows only one player/user, and does not need to communicate with any other EODs, it’s non-joinable.
    • …otherwise it’s very likely joinable. If you use any NPCs or allow more than one player, that is a dead giveaway that it is joinable.

    So what’s the big deal? Why is it so crucial that you choose one or the other right away?

    Two reasons (and they’ve been mentioned before): events and interactions. You just read about how events are sent using VMEODClients. But in joinable EODs the connected VMEODClients can change very often, such as during a game of pizza. We can’t trust that any particular player will still be connected when we want to send an event to the object, so we have to use the special VMEODClient called Controller.

    We also have to keep the Sim(s) connected to the object the whole time they interact with the UIEOD. This is very simple to do with non-joinable EODS—the interaction action tree simply will never complete and return true or false until the player disconnects. But joinable EODs have to use another method—another prim actually—entirely. Let’s start with non-joinable, as they are simpler to understand.

     

    Non-Joinable EODs

    Dressersemiglobal.iff: In the “Change Clothes” interaction, the tree continues looping back to the Invoke Plugin prim indefinitely, until the prim returns true (EOD server disposed). Events of value 1 and 2 are handled but still loop back to the prim once evaluated

    As a reminder, the Invoke Plugin prim returns GOTO_FALSE any time there is an event in the stack of events to be evaluated. This does not include the initial event used to connect a client. If there are no events, the prim actually returns GOTO_FALSE_NEXT_TICK, which means it will wait until the next tick before returning false again. As long as the client stays connected to EOD, the Sim will stay connected to the object because the interaction will never complete. Should the Sim be interrupted by something in the world, such as a collision or low bladder motive, the client will auto disconnect and the EOD server will be disposed. After you’ve determined there are no events to be evaluated, you can do anything you wish with your Sim in that iteration of the GOTO_FALSE from the prim, including checking custom motive levels or simulating them (such as raising fun).

    If you’re making a non-joinable EOD, it doesn’t get much simpler than endlessly looping the action tree of the interaction of your object. Simply specify the values of your events in the Expression prims and handle them accordingly. Use the GOTO_TRUE of the Invoke Plugin prim to handle any aesthetic choices that should execute upon the Sim finishing with the object, such as animations ending. Go and take a look at the interactions of all of the non-joinable EOD objects to see what sorts of things you can do in yours: slots, trunk, dresser, dancefloor, timer, newspaper, gamecompdrawacard, permissiondoor, rack & rackowner, scoreboard, and signs.

     

    Joinable EODs

    Roulettesemiglobal.iff: “Interaction – Play Roulette – Begin” tree allows the client to connect to the EOD server AND pushes another interaction onto the Sim’s queue

    For the many reasons mentioned before, joinable EODs are more complicated in their construction. Let’s tackle each issue at a time, the first one being keeping the Sim connected to the object the whole time they’re interacting with the UIEOD.

    In the picture above, just before the client connects to RouletteEOD you will see that another interaction is pushed into their queue using the Push Interaction prim. Just like when a Sim finishes using the toilet and the “wash hands” interaction is pushed into the queue, you too can push interactions to keep the Sim connected to the object indefinitely. Which interaction is pushed in the example above? Check the tree table for interaction number four.

    The tree table of roulettesemiglobal.iff shows all the interactions available

    “Interaction – Play Roulette – Idle” is pushed into their queue as a head continuation—it’s pushed to the head of the queue to be processed the very next. The current interaction (Play Roulette – Begin) then finishes after the Sim’s client connects using the Invoke Plugin prim. The prim will return false on the next tick (GOTO_FALSE_NEXT_TICK) and then the Sim will process the next interaction in their queue, which is the “Idle” one that was just pushed (pictured below). Note that whether the “Play Roulette – Begin” interaction returns true or false is inconsequential, as long as it ends and clears from the queue of the Sim.

    Roulettesemiglobal.iff: “Interaction – Play Roulette – Idle” will push itself as another interaction onto the tail of the Sim’s queue, in addition to other operations such as simulating the Sim’s motives

    As you can see, yet another interaction is pushed onto the Sim in this “Idle” interaction: it’s pushing a copy of itself onto the Sim. At first glance you may notice two things:

    • This operation does not seem necessary because at the bottom of the tree (pictured below) the interaction never finishes until the sub-routine “End Playing?” returns true
    • The Push Interaction says (Tail Continuation) this time
    Roulettesemiglobal.iff: “Interaction – Play Roulette – Idle” does not complete until the sub-routine “End Playing?” returns true (or an outside force from in the world creates a global notification, such as a fire or low motive/need)

    The reason for the Push Interaction to be present and for it to be (Tail Continuation) are one and the same: other interactions will be pushed into the Sim’s queue during gameplay. These will be interactions that contain animations for the Sim to react to gameplay on the roulette table: making a bet, standing and looking nervous as they wait for the spin, and reacting to winning or losing. We want to push the “Idle” interaction back into their queues after these reactive/animation interactions are processed, otherwise the Sim will leave the table after reacting. If we use (Head Continuation) like before, the “Idle” interaction will always be given priority over the reactive/animation interactions and we’ll never see the latter get executed.

    Keeping the Sim connected to the object is a matter of constantly pushing interactions into their queue, which is achieved using the Push Interaction prim. But now how and where are events handled? As was stated before, we cannot trust that any player’s client will still be connected to the EOD at any point, so we cannot use a player’s client to process events. I already covered how this process looks in the handler class (back in events), but how are they actually received and handled by the object?

    One thing that is not obvious unless you look at the Invoke Plugin class (cf. FSO.SimAntics.Primitives.VMInvokePlugin) is that when a joinable EOD server is created, the first connection is actually from a client that has no avatar. This is a client that actually belongs to the callee object, aka the in-world object. That’s right, the callee object is actually connecting to itself to initiate the EOD server instance. Then the Sims’ clients (players or NPCs) can connect to and disconnect from the EOD at will. The naming convention for this initial avatar-less client is the Controller. Since the controller will always be connected until the EOD server instance is disposed, we can safely send events on its VMEODClient.

    But in order to check for and handle these events, the object needs to periodically (once per tick) invoke the Invoke Plugin prim to check for new events. Thankfully in an object’s Main tree, which can be set in the “Entry Points” tab of any object in Volcanic, we can make the object do whatever we want. In the case of the roulette object, remember that it is a multi-tile object. Opening the object drop-down menu on the top right allows you to find which of the objects has a Main tree declared. This is the tree that is executed once your lot is up and running. It is recommended you use your lead object’s Main tree, as this is the object that the Sims connect to and it will contain accurate attributes that you may need to reference. For more on the lead object and lead tile, check the Attributes section of Locals, Attributes, and Temps, oh My!

    In the Roulette mutli-tile object, the “Roulette – Wheel” object is the smart tile object, a name which denotes that its Main tree contains the necessary sub-routines to effectively process the events for the EOD

    In the Roulette object, the “Roulette – Wheel” is the smart tile object, and it is the only object that has a Main tree declared. Upon examining this tree (pictured below) you will notice that among the many graphical state sub-routines there is one called “Semi-Global: Poll Plugin” that is executed every tick.

    Roulettesemiglobal.iff: “Main” tree (left) and the “Roulette Idle” sub-routine (right)

    This “Poll Plugin” sub-routine (pictured below) is how the object checks its own EOD for new events to handle. If there is no EOD server instance or if there are no events, it returns GOTO_TRUE or GOTO_FALSE_NEXT_TICK, respectively.

    Roulettesemiglobal.iff: “Poll Plugin” sub-routine is where all the events are processed for RouletteEOD. This is accessed once per tick in order to check for new events

    As was mentioned in Locals, Attributes, and Temps, oh My!, the “My” attributes and data does not reference an avatar as the caller, but the object. At the start of this tree, the smart tile object is the stack object, the caller object, and the callee object. This introduces some new challenges in terms of determining which avatar needs to be a recipient of anything related to the event. Any and all animations will need to make use of the Push Interaction prim, as you cannot reference any avatar in this tree without first making them the stack object. Use your event arguments from your plugin class to send the objectid of the avatar upon which you wish to execute any animations or transactions.

    Another obstacle to note is that the Push Interaction prim does not allow the passing of any arguments. You have to denote what avatar (the stack object) is accepting which interaction (literal number) of what object (objectID in a local variable, the literal number denotes which local; 0 = local0, 1 = local1, etc). Take a good look at all of the joinable EODs to help you understand how to make use of this process: pizza, paperchase (code), band, wargame, blackjack, roulette, HoldemCasino, and maze.

    Table of Contents

    8. Ticks, Tips & Tricks about Thread Safety

    What is a Tick?

    A Tick can be seen as a measurement of “Sim Time” even though it’s slightly more complicated than that. In every tick that occurs: objects process their current directive, Sims validate their queued interaction and their absolute position in the lot is updated, sprites are redrawn in both the UI and the world, etc. Quite a lot occurs every tick. I like to think of ticks as heartbeats of the Sims’ world, with each one moving the complicated processes of The Sims Online™ along while advancing time itself. In The Sims Online™ there were 21 ticks every real life minute, but in FreeSO, Sim Time moves a bit faster at 30 ticks per real life minute.

    The magic of the heartbeat of a tick can be utilized in the over-writable method Tick() in your plugin class. Let this method be the central processing unit of your plugin. Each time a tick occurs, a new thread of logic is created. Naturally your class will have tons of threads throughout to catch and handle user input and your EOD’s logic flow. Consider the idea of your EOD having game states where, depending on the current state, user input becomes valid or invalid and your object changes graphics. You would not want to still be processing a game state when it is suddenly changed before the processing is complete. This might cause a race condition which will produce unexpected results based on which condition finishes first. BlackjackEOD is a great example.

    Imagine the following scenario:

    Bob Newbie hops onto an empty blackjack table and decides to make a bet. As he’s pushing the “Submit Bet” button Bella Goth decides to join him at the table and connects to the EOD. In that perfect window between Ticks, Bob’s bet was accepted and the plugin code did not use the thread-safe Tick() method to advance the game, but instead checked for other players in the same method that accepted Bob’s bet. It then changed the game state to Deal Cards and a Simantics event was dispatched that tells the NPC dealer to start the game—but oh no the OnConnection() method detected Bella’s joining of the game and changed the game state back to Betting Round. This allows the two players, Bob and Bella, to use the betting buttons again even while the cards are being dealt. Further chaos ensues as Bella decides to make her bet (and Bob makes his again, thinking his first wasn’t accepted). Then suddenly Bob has paid twice for his bet and Bella was able to bet with cards already dealt. But guess what? Another event was sent to the NPC to deal some cards, so the hand is dealt over. Wow.

    Because you can never anticipate these disaster windows opening between ticks, or users doing the stupidest things like making a bet and leaving the table immediately, or joining an EOD and then disconnecting from FreeSO, etc. you have to be mindful of thread safety.

    BlackjackEOD: The state of the game is only changed in Tick() just before the current state is handled

     

    BlackjackEOD: Even though the game state is changed only in Tick(), it can be flagged for a change anywhere else

    Rhys gave me the idea of using EnqueueGotoState() as a safe means for changing the state of the EOD at the start of the next tick. This way if there are competing state changes, they will only compete for the assignment of the next state, which will only finally be processed in the next tick thread.

    Another very risky place for race conditions are Simoleon Transactions. These are processed in the background and rely on the data service outside of the EOD server. This poses a problem as naturally I can’t let Bob Newbie play blackjack if he doesn’t actually have the money to cover his bet. Only upon the successful deduction of his bet should he be dealt any cards. But I could wind up in trouble if I program the state to change upon a successful transaction, because that could be delayed due to server traffic or lag. This risk increases with the number of Sims that join the EOD, as well, as I need a green light from multiple transaction at this point to change the game state.

    BlackjackEOD: A transaction is queued and upon its success some animation-related SimAntics Events are sent and a new state is enqueued

     

    BlackjackEOD: During Tick() checks are made for each pending transaction to be final before proceeding to deal any cards

    Table of Contents

    9. The User Interface IS the User Experience

    This is the only section dedicated to the creation of the UIEOD. The reason for that is that your best tutorial to figuring out how to make a UIEOD is to look at the ones already made. I decided to put together a few key things I learned throughout my journey of experimenting with the user interface, so perhaps some of this will be useful to you as you begin to discover what is possible and what is worth the effort for your UI.

    Any assets you use in any custom object or UI must be available in your legally obtained copy of The Sims Online™. The creators and administrators of FreeSO condemn the misappropriation or distribution of any Intellectual Property of Electonric Arts, Inc. It is not legal, for example, to take textures from The Sims™ or any of its expansion packs. Any custom textures you wish to use must be made by you. Do not use another artist’s textures, patches, or modifications without express written permission. Violation of any of these standards is a violation of FreeSO’s open source license. Your use of FreeSO and any submittal of code implies your acceptance of and compliance with the license.

     

    Don’t Reinvent the Wheel: Ubiquitous EOD Assets

    As was mentioned before, in order to make your UIEOD visible, you must use an event. Where you put the event in your plugin class is up to you, but it makes a lot of sense to put it in the OnConnection() class so your user sees the UIEOD upon connecting to your object. When you set up your handler method in the UIEOD class, there a specific command to change the “Live Mode” to “EOD Mode” called Controller.ShowEODMode (cf. FSO.Client.UI.Panels.EODs.UIEODController.ShowEODMode). This command accepts an object of type EODLiveModeOpt (cf. FSO.Client.UI.Panels.EODs.UIEODController.EODLiveModeOpt) which contains options for customizing your EOD window using the assets already present in the The Sims Online™ files without you needing to manually allocate them.

    UIEODController.cs: EODLiveModeOpt contains options that will automatically find the texture you need and place it for you for the background of your EOD window
    Textures from the FAR archives unpacked to display most of the options that can be selected in EODLiveModeOpt for Controller.ShowEODMode

    Once you’ve selected the options you’d like for your EOD background, the next step is populating the UI with the images, buttons, and text that you’d like for your EOD to function. Note that images (UIImage or UISlotsImage) and buttons (UIButton) both require textures in order to be drawn. I recommend digging through all the available textures that you’ve unpacked with the FAR unpacker and finding the ones that best fit your design. The process for doing this is detailed next.

    Custom textures are certainly usable, but you must be vigilant that they are designed in the style and milieu of FreeSO (The Sims Online™ or The Sims™). You owe this to the developers as well as the players. Don’t muck up the charm and nostalgia of FreeSO with anachronistic designs. A section below details the process of saving and importing custom textures.

     

    Locating and Allocating Existing Textures

    In order to view all of the textures, you’ll need a FAR archive unpacker. You can download the one in the FreeSO repository. The archives are the .dat files in your TSO installation directory (\The Sims Online\TSOClient\uigraphics\). Select a .dat file to load it in, then extract it somewhere and you’ll see a folder full of bitmap and targa images. Then you can begin browsing all of the UI textures and pick and choose what fits your UIEOD design.

    A look at the textures unpacked from the EOD archive

    In case you haven’t noticed, that particular shade of pink (#ff00ff), which is so widely prevalent in the bitmap files, is treated as transparent when the texture is processed. Pictured above is just the EOD archive, but there are many more archives, some full of textures that were never even used in The Sims Online™. When you’ve found one that you think fits your design (or you at least want to see what it looks like) just make a note of the filename. The next step is to locate the ulong ID associated with that file.

    FileIDs.cs: A enumerated list of unsigned long integers associated with every texture found in the FAR archives

    Personally, I like to just type the filename into the search function of Visual Studio, but you can also do a text search of the FileIDs.cs class.

    Search the filename in Visual Studio to quickly find its ulong in FileIDs.cs

    Once you know the ulong of the texture file, obtaining it is simply: GetTexture(ulong); (cf. FSO.Client.UI.Framework.UIElement.cs).

     

    Importing Custom Textures

    Unpacked textures are in bitmap format, using RGB and 8 bits per channel color mode. Using this knowledge, as well as S1ndle’s amazing artwork, I created custom slot stops and a slot legend for slot machines four and five. I recommend you use the same color settings and file format when making your custom textures.

    Importing the textures is fairly simple. Make sure you have a backup texture should the import fail for any reason. The syntax is pictured below, taken from UISlotsEOD.cs (cf. FSO.Content.Model.TextureRef.cs).

    UISlotsEOD.cs: A file texture reference is created with the local target directory and filename of the custom texture file, and the Texture2D is obtained using theAbstractTextureRef.Get(GraphicsDevice)

    Don’t forget to actually put your custom textures in that directory to be found. To do this, use FSO.Content as was explained in Getting Started. The build action should be Content and Copy if newer.

    FSO.Content: As with .iff and .piff files, put your custom textures in FSO.Content and set the build action to “Content” and “Copy if newer”

     

    Dynamic Custom Textures

    There’s one more method to creating custom textures, and that is by actually drawing their pixels dynamically at run-time. For RouletteEOD, I felt the need to educate the player on how betting worked. I decided that the best method to do that was to highlight which numbers the user was about to bet on based on where their dragged chip was hovering.

    UIRouletteEOD.cs: Semi-transparent black pixels appeared and disappeared based on the user’s dragged chip’s position, demonstrating which numbers were about to receive the bet

    The technical execution involved creating semi-transparent black pixels arranged in boxes and placed on top of each number. These boxes became visible all at once as soon as a new chip was picked up, and then certain boxes became invisible if the numbers beneath them were to be highlighted based on the chip’s position. Rather than importing a black box created in MSPaint, I opted to use the TextureGenerator class and its GetPXWhite() Method. Despite its name suggesting white, any Color can be used as an argument.

    UISlotsImage.cs: The UIHighlightSprite class creates a black-colored texture to the specified width and height and optional alpha (default 0.5)

    Table of Contents

    10. Style, Comments, and Some General Do’s and Don’ts

    Style & Comments

    The style standards for FreeSO aren’t very strict, but start by checking out this document by the main developer.

    As I mentioned before in Introductions, I’m not a programmer by trade (or education) and I came to start working on FreeSO with some Java experience. Style conventions in Java are extremely different than C#, so I had to do a lot of rewriting. When in doubt, first read that document above, and then check out what has come before in existing EOD classes. Be verbose with your comments and have some helpful variable and method names. Don’t use single letters for variables or other lazy things like that. Try to imagine you’re writing something that someone may want to read later in order to learn how to contribute to FreeSO themselves!

     

    Do’s and Don’ts

    At the time of the writing of this guide, the admin team has not yet created a set of standards for the acceptance of custom content. Whenever that is completed, definitely refer to those guidelines when making your custom EOD.

    With regard to your CC EOD object and premise in general:

    • Consider balance and exploitation possibilities: Does your EOD reward a measured amount of money, skill, or motives when compared to other similar objects or EODs?
    • Do not make an EOD that makes another EOD or object obsolete. Consider enhancing existing objects wherever possible.
    • Do not neglect the object and playing/NPC Sims’ behavior while playing the EOD. Even though the UIEOD is the user experience, the details of the object add to the immersion in the world of FreeSO.
    • Do not forget about the motives/needs of the Sims. If your EOD requires the Sims to be connected for super long periods, such as a long game of Spades, make sure the game is short enough to complete after a “greening” session, or have a failsafe in place if someone needs to run to the toilet!

    With regard to your plugin and UIEOD code:

    • Use the UI assets found in the The Sims Online™ files first before making any custom assets.
    • Make custom UI assets that are harmonious with the existing ones—they should be in the millieu of TSO.
    • Never trust a client. Make all checks for secure and valid data on the server side (in the plugin). Players can run modified clients to do illicit things.
    • Separate the actions that truly need a server message (an Event) versus those that can rely on the Timer class. Consider which functions need only be seen by a single user in their client, versus every player in the lot.
    • Make all of your custom strings able to be translated into other languages by putting them in .cst files. Do not use string literals to send information to players, as they cannot be translated.

    Table of Contents

    11. Other Resources

    Custom Content & Volcanic

    3D Object Meshing & Texturing

    General FreeSO & The Sims Online™

    • freeso.org — FreeSO central, including the blog of development, links to installation, and the forums
    • TSOMania — Everything you need to know about FreeSO and The Sims Online™

    Simitone and The Sims™

    Table of Contents

    Appendix A: Updates or Changes

    This section is dedicated to any changes to this guide due to updates with FreeSO, including closed-source management or launcher programs that change how FreeSO is executed or updated. Be sure to check this section periodically if you’re working on an EOD.

    31 July 2018

    17 April 2018

    • Appendix section created

    Table of Contents

    Advertisements