Tuesday, July 2, 2013
FOnline: 2238 source released
After shutting down the FOnline: 2238 server an issue remained bothering the developers of the game: what to do with the code, tools and the custom created content?
The discussion was led, the opinions were presented, the decision was made; after a while we did realise that there is no point in letting the creation hibernate under the virtual layer of dust, hence the release of the source code, which includes above mentioned tools and content, was the only rational way to go. There were various reasons for such decision, but one of them seemed to have an additional, quite heavy impact on the decision itself - it was the fact, that our new endeavours would soon be concentrated on a better promotion of the FOnline engine via our new English speaking developer community fodev.net, on continuing its development into a platform not only for Fallout-like games, but also RPGs in general, and last but not least, to cooperate with everyone who had these (or similar) aims in regard to the engine. It occurred to us as the next logical step in providing the support for many indie projects based on FOnline, as well as in attracting potential developers to start understanding its capabilities and limitations.
We do hope that everyone interested in the FOnline engine will benefit from the release of the FOnline: 2238 source.
Server and related tools
Client + Mapper
More tools (with sources)
https://github.com/rotators/tools
https://github.com/rotators/WitchLord
https://github.com/wipe2238/FOUpdaterEx
Alternative Download (these files will not be updated any longer, and further changes on github won't be included here)
Autogenerated documentation (using Doxygen) (the Reference section is the most interesting there)
For those who cannot wait to run their own server and look around from a whole new perspective, we prepared a short tutorial which should help you with the first steps as an authenticated character in the game world. Have fun!
Friday, June 28, 2013
fodev.net
After shutting down the FOnline: 2238 Server, we have moved the database (as well as some other data) to a new place - fodev.net forum!
Why such a forum? The FOnline scene always lacked a community website in English dedicated to its developers. Informations about SDK are scarce and scattered - we see it as an obstacle in achieving more interest into FOnline. That's why the former FOnline: 2238 devs decided to fill this gap by creating fodev.net. We would like it to become a place for all people focused on creative aspects of the FOnline engine - place where you can discuss developing games, share ideas, ask questions, publish and share your work with others and announce new FOnline projects. A friendly hub for English speaking developers.
If you were registered on the FOnline: 2238 forum you don't have to create a new account - you can use the same login and password.
Of course fodev.net is not the only thing that we would like to attend to. We are still eager to create games. Currently, however, we have decided to focus on something slightly different than a game - creation of a new FOnline development kit with improved scripts and tools. Our aim is to get a better starting point for new projects. Only time will tell if we will be successful. This new endeavor is in very early stage and you can follow it on Github. It's developed as open source.
Speaking about open source... we have more news for you. To answer to many requests of the FOnline: 2238 fans, we have decided to release the source code of our game. We need to clean it up a bit and as soon as this work is done, everyone should be able to download the sources, start a FOnline: 2238 server or create their own projects based on it. This shouldn't take too long, so stay tuned!
Kind regards,
Rotators
Thursday, June 6, 2013
The End
The Server
On Sunday, June 23rd 2013, the FOnline: 2238 server will be shut down. The details are trivial and pointless, the reasons, as always, purely human ones. The interest in the current state of the game is steadily declining, while, at the same time, expectations about further development of the project can't be matched. With a decimated player base and, in turn, low number of donations we can't afford us any further server lease.
For you, the players, this means that there are over two weeks to play and discuss FOnline: 2238 on the current forum. This moment occurs important to many of us and we would like to use this opportunity to invite all the people who have been sharing so many memories from the last four years to login on June 23rd (evening time CET) for a big shutdown party. We definitely would love to have as many people as possible during these very moments and, of course, during the last days of 2238.
We would like to thank the players who supported us with their suggestions, bug reports, patches and content. Special thanks to all wiki editors, moderators and game masters. Last but not least, we would like to thank all our donors, who helped us to keep the 2238 server running for so long! Without you, the last 4 years wouldn't have been the same.
However, this is not the end, merely a new beginning for us - and we hope for some of you as well.
The Forum
The current 2238 forum will be transformed into a "FOnline developer zone" - a community dedicated to game development using the FOnline engine, a place where you can discuss developing games, ask questions about the engine, publish and share your work with others, announce new FOnline projects and maybe even have a whole board for your own project. As an inevitable result of this change, all 2238 game related boards will be closed with everything regarding the "SDK" and "3D Development" expanded. If you have any ideas and suggestions, feel free to share them with all of us.
Currently there is no decision whether the FOnline: 2238 sources will be released or not.
That's all folks, have fun in the last days of 2238!
Saturday, March 3, 2012
Everything has its end, so does the Great Exodus
IP: 176.9.105.171
Port: 2238
Hopefully, all ships will rally at the given coordinates soon. If you have any problems, use the standard procedure and contact the Fleet Network.
Thursday, March 1, 2012
Exodus in progress...

All sections reported in. Expect the hyperspace drive to be triggered any minute now.
Exodus
Game server will be closed later (after we will finish to move website), so that game itself should be available as long as possible (and with shortest possible interruption). More info will be announced as we go.
We hope everything will go smooth and without bigger problems!
Tuesday, February 28, 2012
Server issues - update #4
Monday, February 27, 2012
Shelter
http://www.moddb.com/games/fonline-2238/forum
http://www.indiedb.com/games/fonline-2238/forum
You'll need ModDB/IndieDB/Desura account in order to start posting, so be sure to register (if not already). See you over there!
Server issues - update #3
Again, thanks for your patience and asking for a bit more patience, the rush on server could make things a bit harder for us:)
Sunday, February 26, 2012
Server status - in the meantime...
After the initial problems we've had, the server could have been pinged most of the time and the the low-level host control panels were responding, but booting up the old chap with primary OS was impossible. The technical support was (probably) doing their best to answer to our requests, but the communication was tedious, usually taking too long or resulting in additional waiting intervals. In other words, remote management proved to be quite difficult, which we were counting on, since we're talking about one of the cheapest hosting solutions among those offered by our host. Good news is that the game session and other critical data look good thus far. We will continue working on getting everything back up, but no rational estimations can be given at this point.I'd dare to say that this is not a case without solution and we have been considering several options for quite a time already. Long story short - one of the most intriguing ideas right now is to move to another host, i.e. to find better offer for beans, getting... ehmmm... more (and better) beans in the process. Currently, our critical control point proved to be the above mentioned server management, which provides us with next to none flexibility when it comes to keeping the server up n' running. As all of us already know, a proper server management is necessity for projects like 2238, no more, no less. According to some unofficial feedback, there's a good portion of players and the community interested in backing up such kind of "exodus", which is a good sign and a wind in the back for meeting the final decision. We would like to use this opportunity to thank everyone for supporting us during all good and hard times we've been through together.
Posting comments is temporarily available. Please don't spam, thank you.
Saturday, February 25, 2012
Server issues - update #2
In other news - current situation brings back the latest discussion about changing server, which had been planned for the next few months. We are currently checking various offers and will pick one
In the meantime, we're preparing the various elements, which we have access to, for migration. Once again thanks for patience!
Friday, February 24, 2012
Server issues - update
The problem is, that they've said they're gonna take off the lock in few hours, but they didn't do it till now. Even if they stated so in tech support request message.
So we're still waiting...
Wednesday, February 22, 2012
Server issues
The machine ceased to reply after that. We currently do not know more details about the issue, we'll try to keep you up to date with information.
Thanks for your patience.
Wednesday, January 4, 2012
Server forecast for Friday 06-Jan-2012: Wipe with heavy data loss
Don't forget to let the Brahmin out and burn down your accomodation!
Until our next forecast - have a nice time!
Friday, December 23, 2011
Please read: a personal appeal from the FOnline: 2238 Developer Lisac Twok

I feel like living the last of my days in a polluted, unclean world, infested with bugs, glitches and crashes.
Imagine the world where every human being on the planet could wipe their virtual reality as they want, whenever they want. Although we're currently far away from that vision, it doesn't mean we have to give up on our dreams! Wipe, my brothers and sisters, wipe is our reality.
With the latest features being tested and discussed, with those last bugs being squashed or pulverised with pulse rifle, with each and every stinking breath of the stuck NPCs that break your quests - we are one step closer to our vision! As I write these words, the critical amount of bits and bytes are being accumulated in Paris, waiting to come into contact with a void function that shall release the Big Bang and bring us the next wipe, the salvation! The last of our days are coming, beware!
Not soon, but very soon, the wipe will bring the end of days as we know it and bring a new life to the brave world that has to be born on the ashes of the old one. Very soon.
Gather your forces. Wake up your sleeping moles. Send a telegram to Rangers. Take your minigun from the rusty tribal chest. Get your granpa stand by your side. Because, very, very soon...
It's about to wipe.
Saturday, November 19, 2011
Wanted: Donations - Dead or Alive
As always, Cvet & Rotators are grateful for your donations, as well as for any other great work and feedback provided by the FOnline communities.
Friday, October 14, 2011
Improving the vintage
Over the time it became obvious, that using such 'interface' is really big pain, and the usability of our game is not up to modern standards by a mile or two. But we have left that part unchanged, as there were other things to do (and still are). The upcoming wipe will not change it drastically, but it will surely improve the way factions are managed.
Multiple bases
Of course, not all problems will be fixed immediately, but we're going to allow one faction to own multiple hideouts.
Access
To simplify things, the access will be resolved on per-rank and per-status basis. This means it won't be possible to do it per-player and per-faction (not yet at least), but still, that simple modification should make life of faction leaders much, much easier, and it should increase the importance of proper rank assignment.
Easier status change
We've also added the ability to create list of known factions. Such list is then used to alter the status of all members of said faction at once.It should work well with per-status access for bases. While we still have basic statuses: friend/neutral/enemy/invited, the system should allow for more so we could improve one that. Question is, whether it's gonna be worked on, or whether web-faction-terminal will takeover.
Summary
So why we haven't reworked it from the scratch, possibly creating client-side interface for it? Well, there are few reasons. First of course are, the plans for web faction terminal (which does not rule out easy client-side interface alongside with web interface). The other is that we're still working on GUI internals, our GUI code differs from the default SDK, and we're working on a ways to make them compatible. Sometimes it's good to work on internals first.
Wednesday, September 14, 2011
Living without client
Gathering, once again
As we already mentioned, we are changing (again) the way resources will be gathered. This of course required yet-another-refactor of that part of code. Last time I was doing this, it was rather boring task: refactor script of one facility, start server, login, spawn some of those facilities, spawn myself needed item, use it, check. Rinse and repeat (sometimes you may hot-reload the script, but you need to still test it manually).
This time I decided to write set of test cases for every 'use event' that results in resource being acquired. And then, those tests will check the stuff for me, so that I do not even need to fire up the client. Nifty!
class Plant : IFacility
{
Item@ item;
uint16 resource;
uint batch;
Plant(Item& item, uin16 resource, uint batch)
{
item.SetEvent(ITEM_EVENT_USE_ON_ME, "_UseItem");
item.SetEvent(ITEM_EVENT_SKILL, "_UseSkill");
item.SetEvent(ITEM_EVENT_FINISH, "_Finish");
@this.item = item;
this.batch = batch;
this.resource = resource;
this.batch = batch;
}
int get_Amount() { return item.Val1; }
void set_Amount(int val) { item.Val1 = val; }
bool UseItem(Critter& cr, Item& usedItem)
{
uint pid = usedItem.GetProtoId();
if(pid == PID_KNIFE || pid == PID_COMBAT_KNIFE || pid == PID_LIL_JESUS_WEAPON || pid == PID_THROWING_KNIFE)
{
if(IsOverweighted(cr))
{
cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_OVERWEIGHT);
return true;
}
else
{
cr.AddItem(resource, batch);
cr.SayMsg(SAY_NETMSG, TEXTMSG_TEXT, text);
}
return true;
}
else return false;
}
}
This is an excerpt from a class that represents plant facility that we may cut with knife to use the needed resources. It's represented in-game as an item, and we are initializing it in item script in following way:
void item_init(Item& item, bool firstTime)
{
AddFacility(item, Plant(item, PID_FIBER, 2));
}
It uses the pattern I've described in following post. The Plant constructor assigns the events to the item:
// critter uses skill on facility
bool _UseSkill(Item& item, Critter& cr, int skill)
{
IFacility@ facility = GetFacility(item);
if(valid(facility)) return facility.UseSkill(cr, skill);
return false;
}
// critter uses item on facility
bool _UseItem(Item& item, Critter& cr, Item@ usedItem)
{
if(valid(usedItem))
{
IFacility@ facility = GetFacility(item);
if(valid(facility)) return facility.UseItem(cr, usedItem);
}
return false;
}
// :>
void _Finish(Item& item, bool deleted)
{
IFacility@ facility = GetFacility(item);
if(valid(facility)) RemoveFacility(item);
}
Testing stuff
First thing we would probably like to test is, whether critter really receives the items if using proper tool on that facility.Let's first prepare a helper that's gonna greatly reduce the amount of repeated code:
void mock_CritterAddItem(Critter& cr, uint16 pid, uint count)
{
CallExpectation("CritterAddItem_" + pid + "_" + count);
}
class Fixture
{
Critter@ cr;
Item@ tool;
Item@ item;
Fixture(uint16 tool, string@ script)
{
@cr = MockCritter(1);
Mock("Critter::AddItem", "mock_CritterAddItem");
@this.tool = MockItem(tool);
@this.item = MockItem(1); // this pid does not matter
item.SetScript(script);
}
IFacility@ get_Facility() { return GetFacility(item); }
}
And now, the first test:
void test_PlantFruitGivesProperResource()
{
Fixture fix(PID_KNIFE, "prod_plant_fiber@item_init");
fix.Facility.Amount = 100;
ExpectOnce("CritterAddItem_" + PID_FIBER + "_" + 2);
fix.item.EventUseOnMe(fix.cr, fix.tool);
VerifyExpectations();
}
The most important part is the mock for Critter::AddItem, which just stores the expectation named in following manner: CritterAddItem_PID_COUNT. In above test function, we're preparing a context consisting of:
- critter
- knife
- item representing the plant
Code is already bugged
Oh great:( I'm writing some code snippets that already contain bugs. So what? Let's fix them, but first let's write a test that confirms them, check if it fails, and then fix the bug and re-check the test:
void test_PlantFruitEmpty()
{
Fixture fix(PID_KNIFE, "prod_plant_fiber@item_init");
fix.Facility.Amount = 0;
ExpectNonce("CritterAddItem_" + PID_FIBER);
fix.item.EventSkill(fix.cr, -1);
VerifyExpectations();
}
Since there are gonna be no timeouts on characters, the facilities itself should somehow limit the amount of resources that we can obtain (and they will be regenerated over time). We've already had implemented some Amount property in our Plant class, but the code didn't care about it. And this test proves that - we expect that AddItem is not gonna be called with PID_FIBER (and whatever count) at all, but it is, because we do not check for amount left in UseItem method. Let's fix it:
// ...
else
{
if(this.Amount > 0)
{
cr.AddItem(resource, batch);
cr.SayMsg(SAY_NETMSG, TEXTMSG_TEXT, text);
}
}
// ...
And let's run the test again. It succeeds! We've fixed a bug, we tested it. Without doing manual labor.
The method I'm describing here, really makes me happy developer, as I do not need to spend time on manual testing, and also I am preparing more and more tests that's gonna test whether existing features are still working in the future (you know, some change in other area of code can really break the other part of code, in an unexpected way, better catch at least some of such bugs).
There is still one bug left in the code (probably much more, I wrote this post without compiler). I'm leaving it for readers to find out and write test code that proves it!
Friday, September 9, 2011
Walking the way of bombs
It isn't exactly news that various towns in the wasteland are racked with the crazy nature of suicide bombers. In real life and in FOnline: 2238. This particular way of entertainment is in almost all cases only entertaining for a very small minority of people and due to this, discussions internally and externally are kind of an unofficial sporting event by now.Anyway, to make it short: In the future we want to change the way explosives are working and used in the game, to limit (but not totally remove) the amount of suicide runs. To accomplish this, we internally agreed on the following ideas:
- Guards can "sniff" activated explosives (C4, Dynamite, and placed Landmines).
- The player can't activate explosives (C4, Dynamite, and placing Landmines) whenever a guard is around or if the player is in guard range. (It simply doesn't work, with an entry in the message log.)
- The player can activate explosives (C4, Dynamite and placing Landmines on the ground) whenever a guard is not around or if the player is not in guard range.
- The PC has to activate the explosives far away from guard NPCs,
- then the PC has to run up to their targets (NCR bazaar....),
- and then hopefully explode there before a guard can kill the bomber and take the explosives (disarming them).
Of course, the subject is still open for discussion and suggestions in the forum (for example, there were ideas about involving demolition expert profession in this process). Also we aren't quite sure about when exactly we are able to take this over into the game. It probably won't happen before the next wipe, but I'll hope we can push it afterwards.
Tuesday, September 6, 2011
Testing your server scripts
Why test your code and why should we care? Well, it's very popular approach nowadays, to write a code that tests your code, just to be sure everything works as expected (and it's hard to keep track of everything with large codebase). It's common misconception that's not feasible for games, after all, how can you test something as complex as interactive program - and even worse - with multiple players?
The answer is that you're not writing a code to test your whole game. You're not gonna write a bot that plays it, does all the quests and check if everything still works (and even looks for holes in the walls, though that'd actually be cool). You write the code to test only portions of it, the small pieces, the units. Hence the name: unit testing. There's of course lots of materials on it over the internet, so we're not going to dive into the details, I just describe what were the problems, and how we solved it, so that we can finally try unit tests.
Isolating the code
When you look at the code you'd like to test, first thing that come to mind is that it's probably too complex and involves too much dependencies to be easily testable. Let's look at the example:
bool critter_use_item(Critter& cr, Item& item, Critter@ targetCr, Item@ targetItem, Scenery@ targetScen, uint param)
{
bool isPlayer=cr.IsPlayer();
bool useOnSelf=(not valid(targetCr) && not valid(targetItem) && not valid(targetScen))
// Radio
if(FLAG(item.Flags,ITEM_RADIO) && useOnSelf)
{
if(isPlayer) EditRadioSettings(cr,item);
return true;
}
// ...
}
Say we need to test that when player uses the radio item, he can edit its settings. Unfortunately, to invoke critter_use_skill int this context we need:
- player
- item
Due to above limitations, unit testing your code seems just not feasible. Surely, there must be some solutions, we just need to go out and learn how the world is doing this.
Mocks
The concept is simple. If you need to provide something for the tested code, that's not the part of the test itself but constitutes to the test context, provide a mock. A substitute for real object, a substitute for a function that's gonna allow us to isolate the tested code to the form that may be safely run from within unit test context. As far as above example goes, we would need three mocks:
- player - Critter object with overriden IsPlayer() method that's returning true (another mock actually), or with fields set in a way that IsPlayer() is returning true
- an item - Item object with fields telling us it's a radio
- a function to detect that EditRadioSettings has been called
Mock library
At first I wanted to write some library that would be able to load the script, compile it and execute test functions providing overriden implementations for functions we wanted to mock. However, the AngelScript engine does not allow to re-register any function, so that once we've got our engine set up, we can't register any mocks. We thought of some workarounds, but after some time I decided to modify the server source directly (its angelscript source, to be precise), I suspected it would be very minor modification - so that maintain costs are minimal in the future (keeping it up to date with every server update).Tests Runner server
After some fiddling in AngelScript engine code, I found out that I can easily 'redirect' function call to another script function (whether the original call was meant to invoke script function, or native (engine registered) one. Moreover, it turned out that method calls can be simply redirected to function calls (providing the first argument is the object passed), without any extra work! This way call to bool Critter::IsDead() could be handled by bool critter_IsDead(Critter& cr). Nice!How does it affect our testing capabilities? To put it simply, we ended up with solution that may be used for testing generally, not only for unit tests. Unit tests are designed to test your small pieces of code out there, but hence we are running our tests in full fledged server, we may as well test the system more broadly, we may check how different pieces interact together - whether it works as a whole or not (though I admit, it wasn't my initial goal - I just wanted simple unit testing facility, not integration tests).
But let's get back to the example. Let's start from the last mock we needed, namely, a function substitute for EditRadioSettings. Say, we just want to know, whether the function has been called or not. Let's mock it, and make it so our substitute will indicate that's been called:
void mock_EditRadioSettings()
{
CallExpectation("EditRadioSettings");
}
For above example to work, we need to know what CallExpectation function is doing. It simply increases the call counter, that's stored in some dictionary under the index that's been passed as argument. Later on, we may check that the counter is equal to 1 - that means our function has been called as expected. With such dictionary at hand, we should also define more helpers: Expect(funcname) and VerifyExpectations(). First one is used to remember the fact, that we want the function funcname to be called, and the latter (called at the end of the test), will check it. In fact, we should have the ability to specify the numbers of the call we're expecting:
- ExpectOnce(funcname) - VerifyExpectations will succeed only if function has been called once
- Expect(funcname, count) - success only if called count times
- ExpectNonce(funcname) - success if hasn't been called at all
Mock("EditRadioSettings", "mock_EditRadioSettings")
This function will remember that we want to redirect the call to EditRadioSettings to a function mock_EditRadioSettings.
Ok, what about other mocks? We said that we want an item, and a critter with specific properties and/or we may provide mocks for their methods as well. For this I've implemented simple MockCritter and MockItem functions that spawn the needed objects with only basic properties filled, but for the case of this example, let's assume those are just clean objects - with all properties zeroed out, rest will be handled by method mocks:
bool mock_True()
{
return true;
}
And now, our test function (with needed mocks) in full glory:
bool mock_True()
{
return true;
}
void mock_EditRadioSettings()
{
CallExpectation("EditRadioSettings");
}
void IfPlayerUsesRadioEditRadioSettingsShouldBeCalled()
{
// prepare
Critter@ player = MockCritter();
Mock("Critter::IsPlayer", "mock_True");
Item@ radio = MockItem();
radio.Flags = ITEM_RADIO;
Mock("EditRadioSettings", "mock_EditRadioSettings");
// call tested function
critter_use_item(cr, radio, null, null, null, 0);
// verify
VerifyExpectations();
}
Voila! Now, when we call critter_use_item, it will first call Critter::IsPlayer(), which returns true, even if our Critter structure might have not indicated this, but our mock did. Later, it will check the flags (we've set it on our mocked item) and call the EditRadioSettings, which in fact calls mock_EditRadioSettings. This sets our expectations counter to 1, which is then verified by VerifyExpectations. And then it announces success - we've got our first trivial unit test passed!
The solution we've got is in very early stage, I'm gonna try and involve it in some of the 2238 code testing. We will see if it turns out to be useful.
Enough of this mockery for now!

