Well lets start with a intro , Before i bore you to death with the technical details
this week im really not sure what made me decide that you needed to read me explaining how to rewrite jcoop to add serverside saved scores but im going to anyway. because i thought it was interesting.
Some back story:
My ah monsters server is setup so it is pushed to the point where 225 limits start showing , the game locks up quite a lot on many maps despite running on a pretty high mid-end PC with plenty of ram , The only thing i can do to remedy that would be to lower the monster counts and I dont want to do that , in fact i really want to raise them even higher, But that will need to wait intill 227i comes out with “clientcompmode” as one of my main players insists on still using 226f. ^sigh^
In the mean time my biggest complaint from players isnt that it crashes , as it can usually restart in just a few seconds, but that there scores are lost when it restarts.
now i know that score recovery mod wont work with jcoopz1 because it saves the values in a different value in a custom pri class in jzPRI. and i really didn’t care enough to try and make it work somehow. but a few days ago i decided to look at some of bleeder’s risingsaver code and saw how he choose to save his custom player data , by alliterating the pri classes, ( as oppose to ill iterating the playerpawns which would be how i would have done it) and it just seemed so simple i decided to try and make it work with jcoopz for fun.
Lets do some stuff:
The irst step was to figure out where jcoopz1 stores the values , you can clearly see it is seems to be stored in JZPRI , but that class has been stripped of the values , So utpt is needed to view the values here and it starts to get interesting:
var int EnemyKillCount;
var int FriendKillCount;
var int DeathCount;
var int SuicideCount;
var int jSpree;
var int TScore;
var int STI;
var bool bSTIValid;
var bool bIsTyping;
That a lot of values we can use! Why isnt this data exposed?!
Some are obvious score values, others are validation bools.
While ememykillcount seems usable ,Tscore is what we want to save as it is the total accumulated score that spans maps.
I modified all the code in rising saver and comment out all irrelevant values and change the references to jzpri and go to compile , Oh crap… While zombie did technically leave enough code to recompile subclasses and such he wiped out most of the vars in most of the classes , so the compiler cant find the variables to reference. Hmm what can i do about this , Mabye I can write the entire mod and ask zombie to compile it for me ? ah not really ideal, what other options do i have? Mabye i can add the decompiled variables back into jzpri and recompile so it can reference the variables , YES it will work!. so i do just that.Now we have something we can build against.
At this point i decided that bleeders actual original saving code was too over complicated and overly dependent on 227 callbacks ( There isnt any thing wrong with his code but it used alot of checks that made debuging difficult for me using 225), So i trashed all the data save/load functions and substituted it for my own i wrote a few years ago for my failed ‘statmut’ mod , This has the added bonus of basically making it 99% my own work since the only thing left of his work is a timer call that calls the functions that i wrote and some var definitions that i also completely changed anyway. Also my code is more suited for 225 as it replaces characters that 225 cant understand in player names or save in a ini.
Since what I am building dosnt have access to any kind of mutatenewplayer calls(yet) ,We are doing this data raking somewhat blind (Just by timer) So we need to do something simple to detect when a player joins and there score needs to be re loaded. While technically i could write a pickup to hook into the giveitem and do some magic , i wanted to keep this code serverside. So The simplistic way i thought i could do it was to just compare the score , If the players score is less then the saved value , the score is loaded to the pri. otherwise is is just saved to the server every 15 seconds, why every 15 seconds tho? Well like i said , we are doing it blind so it could take up to a full timer cycle to load the score so 15 seconds was a compimize between resources and load time.
There was a problem Tho , When the level switches the players score is ‘recovered’ every map switch again regardless, But everything apperars to be working, all kills are properly added to that number as things are killed,But It has a bit of weirdness at the map start. hmm lets look a bit further, Decompile jzplayerpawn and you get this and more:
var string SpareHolder;
var string PlayerIP;
var string ConsoleClass;
var string LocalID;
var string EngineVersion;
var string PlayerUID;
var travel int TScore;
var travel string TPlayerIP;
var travel string TLocalID;
var travel string TPlayerUID;
Alot of potentially useful variables. But to get to these we need to repeat what we did before and re add them to jcoopz1.u using unrealed as i dd this i noticed something , 2 more vars labeled Tscore under playerpawn. Lets log some crap and play a bit and see what changes
ScriptLog: ip: 76.117.84.XXX
ScriptLog: ConsoleClass: UMenu.UnrealConsole
ScriptLog: LocalID: -1549992XXX
ScriptLog: PlayerUID: 1467642XXX
ScriptLog: TScore: 14
A bit of logging shows that this tscore seems to be the actual live value we want. It is separate from the totalscore numbers we saved before , that seemed to count the current score, but this value makes up for the difference we saw ( The highscore of 160000 was loaded and replaced the pri values andshowed in scoreboard , But you could tell it was not the current value at mapstar).This big block of data also conveniently gives us a lot of usfull security data we can use to secure the stat saving system from being hijacked.
After further testing it is shown that you need to reload both variables , The playerpawn one is the actual score that travels , and the pri is what is displayed on the scoreboard ( technicly you dont need to set the pri , But it will only update after a mapswitch).
what about security ?
How am i going to secure the data from player hijacking?
I honestly don’t care if someone hijacked another players score , and im pretty sure they are not smart enough to try we have all this data here , but , we cant really ‘trust’ uid t or ip o be permanent so I am currently just using there name. if it was important enough to you you could swap the uid feild for the playername in the code for score table , and let the mod basicly save data via uid and just save the name as the original uid feild to identify the entry.
What I finally decided would be the best plan was to add a option called BeSemiSecure that will validate against the uid saved into the players slot , if that uid matches the slot is used , if the uid’s dont match , a new slot is created for the player .The downside of this is that there can be multiple slots with the same username and if you chose this option , you need to stick with it. If you choose to switch back there will be a ton of duplicate names in the table that will be usefess and need to be manually combined. I did not create this thing primarily for security , so this is a afterthought and was not tested
Outcome and end product:
so what did all this 8 hours of effort end with?
If ends with a actor that can save total score , engine , uid , ip , playername , ememies killled per map and restore that score data after a server crash.
If Your interested in such a thing you can have it becuase thats how i roll.
You need to add it to the mutators or serveractors it’s name is jsaver.jsaver
It works pretty well if all you need is total score saved, or if you want engine version or a sort of half ass player logger to mess with.You wont be able to edit of rebuild this script without modifying your jcoopz files as i stated above so its probably not the best thing to build off but a decent example of how to expand jcoopz with limited means.
I had fun doing this over the last 4 days in my spare time , and I even took the time to make the code well documented and clean. and did some tweaking that increases the code performance quite a bit.