• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Feature Antirollback

anyone know if it protect me from players who crash the server someday and clone items?
 
About the original post, here are some thoughts:

That is a nice idea to save yourself from simple crashes.

Case 1:
C++:
player = nullptr;
...
player->addItem(...);

Here your memory is probably consistent and your server crashed by calling a nullptr.
The fix is probably checking if the player is nullptr before calling addItem, and it's probably safe to save players and restart.

Case 2:
C++:
item = container->getFirstItem();
...
delete item;
...
(do several stuff, and then a player interacts with that container's first item, and server crashes)

Here the item has been deleted, so the memory was eventually used for something else.
The crash happened when calling some function of this item, which had random memory and raised a segfault.
The fix is not that simple, cause you need to know why this item got deleted, but the reference was still there.
Your server's memory is corrupt and we're not sure anymore if it's safe to save.
Saving in this state could lead to saving incorrect amount of the item, incorrect id, or simply raising another segfault during save.

So, I believe this piece of code might be useful for server maintainers that suffer from 'Case 1' crashes.
But be aware to keep your server down until you check why it crashed, fixing it and be sure if that save is safe.

About the save function, you probably wanna kick every player first, before saving them.
Some servers might use the 'onLogout' event to remove temporary quest items, so it doesn't seem right to simply teleport them to temple.

In my case, I wouldn't use this approach.
'Case 1' kind of crashes are rare, because once it happens, it's easy to fix and it'll never happen again. If you know what you're coding and have a decent test server infrastructure, probably this won't worry you much.
'Case 2' would be useful, because once it happens, it might take hours or even days to find out what it happening. You sure don't wanna several rollbacks every day until you find it. However 'Case 2' is not safe to save, so this approach is not useful.

Talking about other approaches, here's what I've in mind:

Idea 1 is logging to a second database in another thread every player's achievements.
If the player kills a monster:
C++:
LogThread::executeQuery("insert into player_exp (player_id, exp,recovered,timestamp) values(1234, 150, 0, unix_timestamp())");
LogThread::executeQuery("insert into player_loot (player_id, itemId,count,recovered,timestamp) values(1234, 9290, 10, 0, unix_timestamp())");

Of course there could be some caching and inserting batches of experience/loot.
The idea here is to log the player's boring work. Such as killing monsters, capturing monsters or whatever repetitive task you have in your server.
Players usually won't mind doing a 30 minute quest again, or crafting some items again.
But for sure they'll mind hunting for 8h the same monster again on the same cave. Or if they're trying to catch a super rare shiny, they finally made it and then the server rollbacks.
If your server crashes rarely enough, the extra loot without expanses won't be a problem. It'll actually be a compensation for other losses.

Idea 2 is having a fork and 3 database system.
Database 1 is the main database which your server is running and Database 2 is a mirror of Database 1 (master/slave).
Every 5-10 minutes you fork your server process, create a new Database 3, lock DB2, copy DB2 to DB3, connects the fork to DB3, kick every player and save it.
This way, DB3 is in a state as if your server had shutdown properly. In case your server crashes, you can copy DB3 to DB1 and lose ~10 minutes of gameplay at most.
It needs some polishing, cause it's not clear what happens if your server crashes during the DB copy. But it seems that it could work.

To finish, remember that there're other reasons for rollbacks. Someone getting infinite rewards from a chest should be a reason to rollback. If that's the case, only the Idea 1 approach would be suitable to recover player data.

PS: in case someone has any ideas on how to solve 'Case 2' crashes, I'd be happy to talk about. Usually I just read some code and try to reproduce it. Which is really hard work, cause we know stuff like 'that item had invalid memory, but what caused it?'
 
Last edited:
@Baxnie While I agree with you in the most part, I don't see having 3 databases and neither saving things in a separate thread as viable options.
The entire save system should be reworked to be event based with priority queues. We would have 'partial' rollbacks that are harmless to server state as we would keep all players in the last priority saved state.

This is what I have though so far, but there's still room for thinking this through:
  • Remove all saves from src (onLogout, onDie.. etc)
  • Use binary and cached save from @Gesior.pl
  • After critical actions (level past X experience and Y%, dying, trading items, selling stuff) we would mark this player cache eligible to be saved.
  • Every X minutes we check the list of all eligible people to be saved and save them to DB.
  • Players that interact with each other must be put both in eligible queue together, to guarantee they will be in the same state.
  • We can have two queues instead of one, one being 'on demand' and as soon as a player or a batch of players enters it they are automatically saved and another that follows the process stated above to wait for the next ~X minutes.
  • In case of having the priorityQueue of previous point, we need to guarantee that players in this queue are also saved along with other players they have interacted in the other queue (otherwise they won't be sharing the same state)

In this approach we guarantee:
  • No dupes, everyone that interacts will share the same state.
  • In each crash, players won't lose anything critical because they will be being saved constantly.
  • Saves are not centralized, which means you won't have lag each server save because you'll be constantly saving people on demand instead of a whole group each half/one hour.
 
Python Exception <class 'gdb.error'> 'saveServer()' has unknown return type; cast the call to its declared return type:

I using ubuntu 20.04
 
Error in sourced command file:
No symbol "g_game" in current context. How to resolve this? TFS 0.3.6
@up

In a manual execution I am getting the following return:
(gdb) call saveServer
$1 = {void (void)} 0x551280 <saveServer()>
but at crash or interruption, it can't find the function saveServer
No symbol "saveServer" in current context.
 
Last edited:
getting this error while trying to compile!!

Code:
/ot/build# sudo cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo ..
-- Disabled: ccache
-- Enabled: ipo
-- Enabled: openmp
-- Disabled: DEBUG LOG
CMake Error at src/CMakeLists.txt:88 (find_package):
  Could not find a package configuration file provided by "cryptopp" with any
  of the following names:

    cryptoppConfig.cmake
    cryptopp-config.cmake

  Add the installation prefix of "cryptopp" to CMAKE_PREFIX_PATH or set
  "cryptopp_DIR" to a directory containing one of the above files.  If
  "cryptopp" provides a separate development package or SDK, be sure it has
  been installed.
 
getting this error while trying to compile!!

Code:
/ot/build# sudo cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo ..
-- Disabled: ccache
-- Enabled: ipo
-- Enabled: openmp
-- Disabled: DEBUG LOG
CMake Error at src/CMakeLists.txt:88 (find_package):
  Could not find a package configuration file provided by "cryptopp" with any
  of the following names:

    cryptoppConfig.cmake
    cryptopp-config.cmake

  Add the installation prefix of "cryptopp" to CMAKE_PREFIX_PATH or set
  "cryptopp_DIR" to a directory containing one of the above files.  If
  "cryptopp" provides a separate development package or SDK, be sure it has
  been installed.
sudo apt-get install libcrypto++-dev
 
i use otserbr, how to proceed with this?

2- compile the src with the flag for gdb
cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo ..

cmake -DCMAKE_TOOLCHAIN_FILE=../../vcpkg/scripts/buildsystems/vcpkg.cmake ..
 
uso otserbr, ¿cómo proceder con esto?

2 - compila el src con la bandera para gdb
cmake -D CMAKE_BUILD_TYPE = RelWithDebInfo ..

cmake -DCMAKE_TOOLCHAIN_FILE = .. / .. / vcpkg / scripts / buildsystems / vcpkg.cmake ..
I have same, you find solution?
 
Sorry to revive, but is there a way to put an instant save per player? example when a player does an action like moving an item, does he receive a save on the character? I'm using nekiro tfs 1.5 8.0 and I suffered sinister rollbacks, where the player is losing level and equipment, can anyone help?
 
Sorry to revive, but is there a way to put an instant save per player? example when a player does an action like moving an item, does he receive a save on the character? I'm using nekiro tfs 1.5 8.0 and I suffered sinister rollbacks, where the player is losing level and equipment, can anyone help?

if you want to save it like that
just use onItemMove function and then player:save()
but im not sure if this can start any lagging if people moving all the time
 
Back
Top