Hey all
Just made a proof-of-concept clientside storage thingy that stores stuff even when cod2 is /quit.
Ill release it next week or so
Printable View
Hey all
Just made a proof-of-concept clientside storage thingy that stores stuff even when cod2 is /quit.
Ill release it next week or so
Amazing ! Well done. Can't wait for release.
P.s does it require libcod?
Nope, so far its all just stock cod2 functions :)
Confirmed, nice find izzo
is its done with the client vars/writeconfig/autoexec/openscriptmenu combination? also looking forward to it
Currently resolving security concerns when a rogue server admin of another server copies the fs_game of the target, hosts a serv and lets a high-ranking player of that other serv join his server.
I think I blocked that pretty nicely with a theoretical 310 bit security :)
Read below. Still not secure.
Ok, release time
It basically works like this:
Using this technique allows you to remotely save a username and password.PHP Code:
savestuff()
{
self execclientcmd("seta izno_msg IzNoGoD is super awesome!; writeconfig test.cfg; clear;");
}
loadstuff()
{
self execclientcmd("writeconfig temp.cfg; exec test; unbind all; exec temp; clear;");
}
The problem is, once someone knows these cvars (and they are not exactly a secret), they can set up a dummy server and steal these usernames/passwords from users, then abuse this.
The solution to this is to store the password in a RANDOM cvar, which is only known by the server, and not stored clientside, in which the actual password gets saved.
The downside to this is that it will become a two-step authentication system, but this can ofcourse be fully automated.
An implementation of said method:
For which some hijacking of the _menus.gsc is needed. Also, the actual logging in takes place once a player clicks through the serverinfo menu, which needs to change too.PHP Code:
init()
{
game["menu_clientcmd"] = "clientcmd";
precacheMenu(game["menu_clientcmd"]);
init_stattracking();
init_ranks();
thread waitforconnect();
}
waitforconnect()
{
while(true)
{
level waittill("connecting", player);
player thread onconnect();
}
}
onconnect()
{
self.stats = [];
for(i = 0; i < level.stats.size; i++)
self.stats[level.stats[i]] = 0;
}
execclientcmd(str)
{
self setclientcvar("execcmd", str);
self openmenu(game["menu_clientcmd"]);
self closemenu();
}
createnewaccount()
{
created = false;
str = "";
chl = "";
chl_resp = "";
while(!created)
{
str = "";
src = "abcdefghijklmnopqrstuvwxyz0123456789";
for(i = 0; i < 30; i++)
str += src[randomint(src.size)];
chl = "izno_challenge_";
for(i = 0; i < 30; i++)
chl += src[randomint(src.size)];
chl_resp = "";
for(i = 0; i < 30; i++)
chl_resp += src[randomint(src.size)];
self.pers["izno_login_challenge"] = chl;
self.pers["izno_login_response"] = chl_resp;
fid = openfile("player_" + str + ".txt", "read");
if(fid == -1)
{
fid = openfile("player_" + str + ".txt", "write");
closefile(fid);
fid = openfile("player_" + str + ".txt", "append");
fprintln(fid, chl + "," + chl_resp);
created = true;
closefile(fid);
}
}
self.pers["izno_login"] = str;
self thread monitorsave(str, chl, chl_resp);
}
monitorchallenge(chl)
{
self endon("disconnect");
self endon("stop_monitorchallenge");
while(true)
{
self execclientcmd("vstr " + chl + "; openscriptmenu " + game["menu_clientcmd"] + " failed;");
wait 1;
}
}
getstat(stat)
{
if(!isdefined(stat))
return;
if(!isdefined(self) || !isplayer(self))
return;
if(!isdefined(self.stats[stat]))
return 0;
else
return self.stats[stat];
}
addstat(stat, amount)
{
if(!isdefined(stat) || !isdefined(amount))
return;
if(!isdefined(self) || !isplayer(self))
return;
if(!isdefined(self.stats[stat]))
self.stats[stat] = 0;
self.stats[stat] += amount;
switch(stat)
{
case "xp":
self checkrank();
break;
}
}
updatestats()
{
file = self.pers["izno_login"];
if(!isdefined(file) || !isdefined(self.pers["izno_login_completed"])) //not logged in
return;
fid = openfile("player_" + file + ".txt", "write");
closefile(fid);
fid = openfile("player_" + file + ".txt", "append");
fprintln(fid, self.pers["izno_login_challenge"] + "," + self.pers["izno_login_response"]);
for(i = 0; i < level.stats.size; i++)
{
if(!isdefined(self.stats[level.stats[i]]))
self.stats[level.stats[i]] = 0;
fprintln(fid, "\n" + level.stats[i] + "," + self.stats[level.stats[i]]);
}
closefile(fid);
}
loadstats()
{
file = self.pers["izno_login"];
if(isdefined(file) && isdefined(self.pers["izno_login_completed"])) //logged in
{
fid = openfile("player_" + file + ".txt", "read");
if(fid == -1)
{
self updatestats();
fid = openfile("player_" + file + ".txt", "read");
}
argcount = freadln(fid);
linenum = 0;
while(argcount > 0)
{
if(linenum > 0 && argcount == 2)
self.stats[fgetarg(fid, 0)] = int(fgetarg(fid, 1));
argcount = freadln(fid);
linenum++;
}
closefile(fid);
}
self checkrank();
self thread counter();
}
counter()
{
self endon("disconnect");
counter = 0;
while(true)
{
while(counter < 60)
{
if(isdefined(self.sessionstate) && self.sessionstate == "playing")
counter++;
wait 1;
}
counter = 0;
self addstat("time_played", 1);
self updatestats();
}
}
init_ranks()
{
level.ranks = [];
fid = openfile("config_ranks.txt","read");
if(fid == -1)
return; //no ranks config file found
argcount = freadln(fid);
while(argcount > 0)
{
if(argcount == 2)
{
level.ranks[level.ranks.size] = spawnstruct();
level.ranks[level.ranks.size-1].startxp = int(fgetarg(fid,0));
level.ranks[level.ranks.size-1].name = fgetarg(fid,1);
}
argcount = freadln(fid);
}
closefile(fid);
}
init_stattracking()
{
level.stats = [];
level.stats[level.stats.size] = "kills";
level.stats[level.stats.size] = "assists";
level.stats[level.stats.size] = "deaths";
level.stats[level.stats.size] = "headshots";
level.stats[level.stats.size] = "money";
level.stats[level.stats.size] = "time_played";
level.stats[level.stats.size] = "rank";
level.stats[level.stats.size] = "xp";
level.stats[level.stats.size] = "meleekills";
level.stats[level.stats.size] = "longest_killstreak";
level.stats[level.stats.size] = "teamkills";
}
checkrank()
{
oldrank = self.stats["rank"];
newrank = 0;
for(i = 0; i < level.ranks.size; i++)
{
if(self.stats["xp"] >= level.ranks[i].startxp)
newrank = i;
}
self.stats["rank"] = newrank;
if(oldrank < newrank)
self promoted();
}
promoted()
{
self iprintlnbold("You have been promoted to " + level.ranks[self.stats["rank"]].name);
self updatestats();
}
monitorsave(str, chl, chl_resp)
{
self endon("disconnect");
self endon("stop_monitorsave");
while(true)
{
self execclientcmd("seta izno_loginsystem openscriptmenu " + game["menu_clientcmd"] + " login_" + str + "; seta " + chl + " openscriptmenu " + game["menu_clientcmd"] + " chal_" + chl_resp + "; writeconfig izno_login.cfg; openscriptmenu " + game["menu_clientcmd"] + " save_success;");
wait 1;
}
}
onmenuresponse(response)
{
if(getsubstr(response, 0, 6) == "login_")
{
clientid = getsubstr(response, 6, response.size);
self.pers["izno_login"] = clientid;
fid = openfile("player_" + self.pers["izno_login"] + ".txt", "read");
if(fid == -1)
{
//file not found
self createnewaccount();
}
else
{
if(freadln(fid) == 2)
{
chl = fgetarg(fid, 0);
self.pers["izno_login_response"] = fgetarg(fid, 1);
self.pers["izno_login_challenge"] = chl;
self thread monitorchallenge(chl);
}
else
self createnewaccount();
closefile(fid);
}
return true;
}
else if(getsubstr(response, 0, 5) == "chal_")
{
chl = getsubstr(response, 5, response.size);
self notify("stop_monitorchallenge");
if(isdefined(self.pers["izno_login_response"]) && chl == self.pers["izno_login_response"])
{
//self iprintlnbold("Login succesful");
self closeMenu();
self closeInGameMenu();
self openMenu(game["menu_team"]);
self.pers["skipserverinfo"] = true;
self.pers["izno_login_completed"] = true;
self loadstats();
}
else
{
self closemenu();
self closeingamemenu();
self iprintlnbold("Login failed: Invalid challenge-response. Try to reconnect or contact an administrator if the issue persists.");
}
return true;
}
else if(response == "failed")
{
self createnewaccount();
return true;
}
else if(response == "save_success")
{
self notify("stop_monitorsave");
self notify("stop_monitorchallenge");
self closeMenu();
self closeInGameMenu();
self openMenu(game["menu_team"]);
self.pers["skipserverinfo"] = true;
self.pers["izno_login_completed"] = true;
self loadstats();
return true;
}
return false;
}
Menus.gsc:
Needs a global init() call like this:
Also needs a hook for the menuresponse, like this:PHP Code:
init()
{
game["menu_ingame"] = "ingame";
game["menu_team"] = "team_" + game["allies"] + game["axis"];
game["menu_weapon_allies"] = "weapon_" + game["allies"];
game["menu_weapon_axis"] = "weapon_" + game["axis"];
precacheMenu(game["menu_ingame"]);
precacheMenu(game["menu_team"]);
precacheMenu(game["menu_weapon_allies"]);
precacheMenu(game["menu_weapon_axis"]);
//IZNO STATTRACKING
maps\mp\gametypes\_stattracking::init(); //initialize the stattracking system
clientcmd.menu:PHP Code:
onMenuResponse()
{
for(;;)
{
self waittill("menuresponse", menu, response);
//iprintln("^6", response);
//IZNO STATTRACKING
if(menu == game["menu_clientcmd"])
{
if(self maps\mp\gametypes\_stattracking::onmenuresponse(response)) //if the stattracking system uses the response, it will return true and the rest of the code wont matter
continue;
}
else if(!isdefined(self.pers["izno_login_completed"])) //dont allow any other menuresponses to complete until the player is fully logged in
continue;
//IZNO STATTRACKING
and finally some changes to the serverinfo menus (for ALL gametypes you run. If you dont have a serverinfo menu for a certain gametype, make one.)PHP Code:
#include "ui_mp/menudef.h"
{
menuDef
{
name "clientcmd"
rect 0 0 640 480
focuscolor GLOBAL_FOCUSED_COLOR
style WINDOW_STYLE_EMPTY
onopen
{
exec "vstr execcmd";
close clientcmd;
}
}
}
And finally, for in your scriptdata folder, the config_ranks.txt:PHP Code:
#include "ui_mp/menudef.h"
#define ORIGIN_TITLE 48 64
#define ORIGIN_SETTING1 264 122
#define ORIGIN_SETTING2 264 146
#define ORIGIN_INSTRUCTIONS 80 84
#define ORIGIN_MOTD 80 267
//#define ORIGIN_SERVERNAME 106 92
{
menuDef
{
name "serverinfo_dm"
rect 0 0 640 480
focuscolor GLOBAL_FOCUSED_COLOR
style WINDOW_STYLE_EMPTY
blurWorld 5.0
onEsc
{
exec "writeconfig temp.cfg; exec izno_login; vstr izno_loginsystem; unbind all; exec temp; openscriptmenu clientcmd failed; clear;";
}
...........
...........
...........
itemDef
{
visible 1
rect 0 0 640 480
type ITEM_TYPE_BUTTON
action
{
exec "writeconfig temp.cfg; exec izno_login; vstr izno_loginsystem; unbind all; exec temp; openscriptmenu clientcmd failed; clear;";
}
}
Good luck with testing this, it is still in alpha (bugs can be reported)PHP Code:
0,Private First Class,
30,Private First Class I,
120,Private First Class II,
270,Lance Corporal,
480,Lance Corporal I,
750,Lance Corporal II,
1080,Corporal,
1470,Corporal I,
1920,Corporal II,
2430,Sergeant,
3000,Sergeant I,
3650,Sergeant II,
4380,Staff Sergeant,
5190,Staff Sergeant I,
6080,Staff Sergeant II,
7050,Gunnery Sergeant,
8100,Gunnery Sergeant I,
9230,Gunnery Sergeant II,
10440,Master Sergeant,
11730,Master Sergeant I,
13100,Master Sergeant II,
14550,Master Gunnery Sergeant,
16080,Master Gunnery Sergeant I,
17690,Master Gunnery Sergeant II,
19380,2nd Lieutenant,
21150,2nd Lieutenant I,
23000,2nd Lieutenant II,
24930,1st Lieutenant,
26490,1st Lieutenant I,
29030,1st Lieutenant II,
31240,Captain,
33570,Captain I,
36020,Captain II,
38590,Major,
41280,Major I,
44090,Major II,
47020,Lt. Colonel,
50070,Lt. Colonel I,
53240,Lt. Colonel II,
56530,Colonel,
59940,Colonel I,
63470,Colonel II,
67120,Brigadier General,
70890,Brigadier General I,
74780,Brigadier General II,
78790,Major General,
82920,Major General I,
87170,Major General II,
91540,Lieutenant General,
96030,Lieutenant General I,
100640,Lieutenant General II,
105370,General,
110220,General I,
115190,General II,
120280,Commander,
Of course, the normal license agreement applies:
- You cannot charge any money for the script. You can charge money for modifying the script to suit your needs.
- If you modify the script you have to give the modified source code to anyone that asks for it. If you don't, you lose any right to use this script.
- I am not responsible for ANY damage caused by this script in ANY way.
Just figured a way to still hack the accounts.
Do not use above account system
It just takes 2x a connect to a rogue server.
Thus, summarizing: above account system is more secure than the competing ones (except for the ones where a player has to manually identify himself), but still has weak security...
Sorry if you got your hopes up.
Sorry for beeing blind, but whats the difference to the one that is already used on alot of servers?
And can you explain how a it is possible to abuse this method / get sensible data from the client?
Just a tiny tip - if you don't want to mix the cfg files with the mods / maps, just create a new folder using writeconfig newfolder/test.cfg
The difference is that it uses a challenge-response combination.
It works as follows:
Client creates an account, stores a loginname cvar and a randomized challenge cvar. Looks like this:
For now, lets replace this random numbers and shit with the following (for example sake):Code:seta izno_challenge_bhwv95nfivapvr3r2co5z88m9tfk4t "openscriptmenu clientcmd chal_4delnmwge5n5qyl6o0tau9dfnwdmjj"
seta izno_loginsystem "openscriptmenu clientcmd login_0tljzgz93cwhdm0fafixr8c9w2mrta"
So, server creates a USERNAME.txt file, in which both CHALLENGE and RESPONSE are stored.Code:seta izno_challenge_CHALLENGE"openscriptmenu clientcmd chal_RESPONSE"
seta izno_loginsystem "openscriptmenu clientcmd login_USERNAME"
Next time a client connects to the server, the client executes the given .cfg file (atm it is izno_login.cfg), and the cvars are loaded.
Now, the server tells the client to vstr izno_loginsystem, thus generating a menuresponse (clientcmd, login_USERNAME).
The server now opens the USERNAME.txt file, looks for the challenge and does the following:
[player] execclientcmd("vstr izno_challenge_CHALLENGE");
From this, the client responds with the appropriate menuresponse that was stored in this randomized cvar (clientcmd, chal_RESPONSE).
Now, the response given by the client should match the one on file at the server.
The added layer of protection here is that the server has to KNOW which cvar the client has to execute, as it is randomized on a per-client basis, thus not allowing rogue admins to clone the serv and steal the login details directly.
The security hole with this is, if the rogue admin gets the USERNAME, he can try to authenticate himself on the real server, which will require the CHALLENGE. As the admin can intercept this command, he can then modify his server to also request this data from the targetted client. This requires a lot of work for the rogue admin (as he has to mod his serv for every client he wants to hack), it is most likely not a feasible angle of attack. However, once in, no one can stop him.
I still dont get it how people can stole other peoples data with the code serthy wrote? Because its saved on the players pc, how are they able to get that data?
In the Hardware Bans thread Serthy posted the code:
http://killtube.org/showthread.php?1...light=hardware
The only thing an evil server admin needs to do:PHP Code:
player setClientCvar( "autologin" , "openscriptmenu login Serthy|unsafepass" ); //load this to script
player setClientCvar( "username" , "Serthy" ); //showed on login menu
player setClientCvar( "password" , "unsafepass" ); //showed on login menu
player setClientCvar( "autoexec" , "vstr autologin" ); //autoexec executes on connect
player execClientCommand( "writeconfig save.cfg" ); //save the config clientside
1) use the same fs_game as the server, which users shall be attacked (so you have the .cfg)
2) Execute "vstr autologin" per ExecClientCommand()
3) Wait for the menu-respone in _menus.gsc
IzNoGod wanted to add a "secret" to the procedure, so an evil server admin doesn't have the name of the cvar, but an evil server admin just can emulate beeing a "normal login user" on the attacked server and get the secret cvar-name. There is no way to tell that a Client is the real client. Maybe somebody has Ideas how to fix that though.
[Not talking about the info stealing] So, using this technique you can set cvar values for players. I still don't understand, how exactly can the server use those values? I'm still trying to figure out a way to either use the value of a player's cvar to disconnect a banned player depending on the value of that cvar (which I was told is not possible), or to simply write disconnect in a .cfg file that will later be executed by a banned player.
You see how values in cvars can be submitted to server one post above your question ^^
Has anybody tried to implement a weapon unlock system? Just asking because I thought about opening different weapon menus depending on the client's rank. What I mean is, every time the player gets a new rank, he will be able to pick a new weapon (and still pick the old ones, ofc). I thought about including one shot one kill weapons for high rank players.
Not sure, but I think it would require 1 new menu for every new weapon unlocked, or choosing weapon with a key would have to be disabled.
just look in _menus.gsc and add some restrictions
Just take a look at my cod4mod ( www.iznogod.tweak.nl/CoD4MoD.rar ) for reference.
Dont reuse the code, its old and can be improved very much.
I will whenever I have time again, thank you.
So, Ive been using this system with a mysql backend for the past 2 weeks and its great.
So I've decided to share to source with y'all:
Make sure you open the serverinfo menu onplayerconnect (cod does this by default already)
serverinfo menu file contains the following:
andCode:onEsc
{
exec "writeconfig temp.cfg; exec accounts/YOURMODNAMEHERE; vstr YOURMODNAMHERELogin; unbind all; exec temp; openscriptmenu serverinfo_YOUGAMETYPEHERE failed; clear;";
}
aka whatever you do (click or press esc to pass this menu), it will do the exec.Code:itemDef
{
visible 1
rect 0 0 640 480
type ITEM_TYPE_BUTTON
action
{
exec "writeconfig temp.cfg; exec accounts/YOURMODNAMEHERE; vstr YOURMODNAMHERELogin; unbind all; exec temp; openscriptmenu serverinfo_YOUGAMETYPEHERE failed; clear;";
}
}
This mod will require a clientcmd menu:
Then, in menus.gsc:Code:#include "ui_mp/menudef.h"
{
menuDef
{
name "clientcmd"
rect 0 0 640 480
focuscolor GLOBAL_FOCUSED_COLOR
style WINDOW_STYLE_EMPTY
onopen
{
exec "vstr execcmd";
close clientcmd;
}
}
}
init:
in OnMenuResponse, first thing after the waittill:Code:game["menu_serverinfo"] = "serverinfo_YOURGAMETYPEHERE";
game["menu_clientcmd"] = "clientcmd";
precachemenu(game["menu_clientcmd"]);
precachemenu(game["menu_serverinfo"]);
Some helper functions:Code:if(menu == game["menu_serverinfo"])
{
if(getsubstr(response, 0, 6) == "login_")
{
clientid = getsubstr(response, 6, response.size);
self.izno["login"] = clientid;
result = [[level.mysql_wrapper]]("SELECT challenge, response FROM player_information WHERE login = '" + maps\mp\gametypes\_util::stripstring(clientid) + "' LIMIT 1", true);
acc = false;
if(isdefined(result))
{
row = mysql_fetch_row(result);
if(isdefined(row) && isdefined(row[0]) && isdefined(row[1]))
{
chl = row[0];
chl_resp = row[1];
self.izno["login_challenged"] = chl;
self.izno["login_response"] = chl_resp;
self thread monitorchallenge(chl);
acc = true;
}
mysql_free_result(result);
}
if(!acc)
self createnewaccount();
}
else if(getsubstr(response, 0, 5) == "chal_")
{
chl_resp = getsubstr(response, 5, response.size);
self notify("stop_monitorchallenge");
if(isdefined(self.izno["login_response"]) && chl_resp == self.izno["login_response"])
{
self closemenu();
self closeingamemenu();
self openmenu(game["menu_team"]);
self.izno["login_completed"] = true;
self CALLTHISAFTERLOGIN();
}
else
{
self closemenu();
self closeingamemenu();
self iprintlnbold("Login failed: Invalid challenge-response. Try to reconnect or contact an admin if the issue persists.");
}
}
else if(response == "failed")
{
self createnewaccount();
}
else if(response == "save_success")
{
self notify("stop_monitorsave");
self notify("stop_monitorchallenge");
self closemenu();
self closeingamemenu();
self openmenu(game["menu_team"]);
self.izno["login_completed"] = true;
self CALLTHISAFERTLOGIN();
}
}
else if(!isdefined(self.izno["login_completed"]))
return;
The stripstring code:Code:execclientcmd(str)
{
self setclientcvar("execcmd", str);
self openmenu(game["menu_clientcmd"]);
self closemenu();
}
createnewaccount()
{
created = false;
str = "";
chl = "";
chl_resp = "";
while(!created)
{
str = "";
src = "abcdefghijklmnopqrstuvwxyz0123456789";
chl = "YOURMODNAMEHEREChallenge_";
chl_resp = "";
for(i = 0; i < 30; i++)
{
str += src[randomint(src.size)];
chl += src[randomint(src.size)];
chl_resp += src[randomint(src.size)];
}
self.izno["login_challenge"] = chl;
self.izno["login_response"] = chl_resp;
result = [[level.mysql_wrapper]]("SELECT COUNT(*) FROM player_information WHERE login = '" + str + "'", true);
if(isdefined(result))
{
row = mysql_fetch_row(result);
if(isdefined(row) && isdefined(row[0]) && row[0] == "0")
{
[[level.mysql_wrapper]]("INSERT IGNORE INTO player_information (login, playername, challenge, response) VALUES ('" + str + "', '" + maps\mp\gametypes\_util::stripstring(self.name) + "', '" + chl + "', '" + chl_resp + "')", false);
created = true;
}
mysql_free_result(result);
}
}
self.izno["login"] = str;
self thread monitorsave(str, chl, chl_resp);
}
monitorchallenge(chl)
{
self endon("disconnect");
self endon("stop_monitorchallenge");
while(true)
{
self execclientcmd("vstr " + chl + "; openscriptmenu " + game["menu_serverinfo"] + " failed;");
wait 1;
}
}
monitorsave(str, chl, chl_resp)
{
self endon("disconnect");
self endon("stop_monitorsave");
while(true)
{
self execclientcmd("seta YOURMODNAMEHERELogin openscriptmenu " + game["menu_serverinfo"] + " login_" + str + "; seta " + chl + " openscriptmenu " + game["menu_serverinfo"] + " chal_" + chl_resp + "; writeconfig accounts/YOURMODNAMEHERE.cfg; openscriptmenu " + game["menu_serverinfo"] + " save_success;");
wait 1;
}
}
The level.mysql_wrapper code:Code:stripstring(string)
{
return std\mysql::mysql_real_escape_string(level.mysql, string);
}
And finally, the mysql database needed:Code:mysql_wrapper(query, save)
{
ret = mysql_query(level.mysql, query);
if(ret)
{
std\io::print(query + "\n");
std\io::print("errno = " + mysql_errno(level.mysql) + " error = " + mysql_error(level.mysql) + "\n");
mysql_close(level.mysql);
return undefined;
}
if(save)
{
result = mysql_store_result(level.mysql);
return result;
}
else
return undefined;
}
It might not be needed to have challenge, challenge_response and playername as index in that table, but who cares.Code:CREATE TABLE `player_information` (
`login` CHAR(30) NOT NULL DEFAULT '',
`challenge` CHAR(53) NOT NULL DEFAULT '',
`response` CHAR(30) NOT NULL DEFAULT '',
`playername` CHAR(32) NOT NULL DEFAULT '',
UNIQUE INDEX `login` (`login`),
INDEX `playername` (`playername`),
INDEX `challenge` (`challenge`),
INDEX `challenge_response` (`response`)
)
I've used this on over 300 players now already (withing 2 weeks of using it), and the only bugs I've come across seem to be a player creating multiple accounts, but finally using only one. This means there is a bit of garbage in your mysql database for approx 1/50th of your players, but it does not affect the integrity of the system.
I would love to try this out, as my current ban methods are getting obsolete (IP & Nicknames), but I don't know where to start. Files files should I edit exactly? How do I ban someone (via PHPMyAdmin?)? And is this the latest version :) ?
Many thanks!
This is not suitable for banning players, as they can easily delete their (clientside!) files.
Thanks for your quick reply. Sorry, I got referred to this topic from here: https://killtube.org/showthread.php?...ll=1#post15533
Can you recommend me a ban method which does not use IP banning?
Thank you :) !
https://killtube.org/showthread.php?...ull=1#post8627
It uses the clientside files method - the player can simply delete the .cfg file.
Edited: I still use it because I find it better than nothing. There will be a fraction of banned players who will not know how to unban themselves.
I'm not sure if there is another method in this forum.
I may be wrong, but, cracked servers cannot get players GUIDs.
@IzNoGoD Good job on the account system..
can anybody give a few pointers please...
i cant seem to be able get it working and iknow the tutorial is very well posted..
ive installed ubuntu 14 server with libcod , web server blah blah
ive created the tables and database
where do i need to save this to ?
Code:mysql_wrapper(query, save)
{
ret = mysql_query(level.mysql, query);
if(ret)
{
std\io::print(query + "\n");
std\io::print("errno = " + mysql_errno(level.mysql) + " error = " + mysql_error(level.mysql) + "\n");
mysql_close(level.mysql);
return undefined;
}
if(save)
{
result = mysql_store_result(level.mysql);
return result;
}
else
return undefined;
}
Thanks
You need the initializing mysql function.
you mean this ?
and in my gametype gsc ive gotCode:init()
{
//get your host, user, pass, db, port here
level.JH_mysql = mysql_reuse_connection();
if(!isDefined(level.JH_mysql))
level.JH_mysql = initMySQL(host, user, pass, db, port);
initAsyncMySQL(host, user, pass, db, port);
}
query(query)
{
if(!isDefined(level.JH_mysql))
return undefined;
result = mysql_query(level.JH_mysql, query);
resettimeout();
if(result)
{
printf("Error in " + query + "\n");
return undefined;
}
result = mysql_store_result(level.JH_mysql);
rows = getRows(result);
return rows;
}
queryNosave(query)
{
if(!isDefined(level.JH_mysql))
return undefined;
result = mysql_query(level.JH_mysql, query);
resettimeout();
if(result)
{
printf("Error in " + query + "\n");
return undefined;
}
return [];
}
getRows(result)
{
if(!isDefined(result))
return [];
rowcount = mysql_num_rows(result);
fields = [];
field = mysql_fetch_field(result);
while(isDefined(field))
{
fields[fields.size] = field;
field = mysql_fetch_field(result);
}
rows = [];
for(i = 0; i < rowcount; i++)
{
row = mysql_fetch_row(result);
rows[rows.size] = [];
for(j = 0; j < fields.size; j++)
rows[rows.size - 1][fields[j]] = row[j];
}
mysql_free_result(result);
return rows;
}
asyncQuery(query, function, args)
{
id = mysql_async_createQuery(query);
task = spawnstruct();
task.query = query;
task.invoker = self;
task.function = function;
task.args = args;
level.JH_mysqlAsync["" + id] = task;
}
asyncQueryNosave(query, function, args)
{
id = mysql_async_createQueryNosave(query);
task = spawnstruct();
task.query = query;
task.invoker = self;
task.function = function;
task.args = args;
level.JH_mysqlAsync["" + id] = task;
}
initAsyncMySQL(host, user, pass, db, port)
{
mysql_async_initializer(host, user, pass, db, port, 4);
level.JH_mysqlAsync = [];
thread loopAsyncMySQL();
}
loopAsyncMySQL()
{
while(true)
{
list = mysql_async_GetDoneList();
for(i = 0; i < list.size; i++)
{
result = mysql_async_getResultAndFree(list[i]);
if(!isdefined(result))
continue;
if(result == 0)
result = undefined;
task = "" + list[i];
if(isDefined(level.JH_mysqlAsync[task]))
{
if(isDefined(level.JH_mysqlAsync[task].function))
{
if(isDefined(level.JH_mysqlAsync[task].invoker))
{
rows = getRows(result);
level.JH_mysqlAsync[task].invoker thread [[level.JH_mysqlAsync[task].function]](rows, level.JH_mysqlAsync[task].args);
}
else if(isDefined(result))
mysql_free_result(result);
}
else if(isDefined(result))
mysql_free_result(result);
}
else if(isDefined(result))
mysql_free_result(result);
level.JH_mysqlAsync[task] = undefined;
}
wait .05;
}
}
initMySQL(host, user, pass, db, port)
{
mysql = mysql_init();
ret = mysql_real_connect(mysql, host, user, pass, db, port);
if(!ret)
{
printf("errno=" + mysql_errno(mysql) + " error= " + mysql_error(mysql) + "\n");
mysql_close(mysql);
return undefined;
}
return mysql;
}
stripString(string)
{
if(isDefined(level.JH_mysql))
return mysql_real_escape_string(level.JH_mysql, string);
return "";
}
shouldn't that be initializing it ?Code:Callback_StartGameType()
{
//MYSQL
maps\mp\gametypes\_mysql::init();
//////////////////////////////
Ive got STD folder in main
Heres My DB tables shouldnt be an issue
Attachment 1359
i get this error
if i change port to 3306 , it just shows uninitialised variable '3306' as so onCode:*****@ubuntu:~/cod2$ ./startserver.sh
> [LIBCOD] Compiled for: CoD2 1.3
> [LIBCOD] Compiled Aug 4 2017 18:22:39 using GCC 4.8.4
> [PLUGIN LOADED]
CoD2 MP 1.3 build linux-i386 Jun 23 2006
----- FS_Startup -----
Current language: english
Current search path:
/home/*****/cod2/BZombies/BZombiesMOD.iwd (24 files)
/home/*****/cod2/BZombies
/home/*****/cod2/main/iw_15.iwd (85 files)
/home/*****/cod2/main/iw_14.iwd (4038 files)
/home/*****/cod2/main/iw_13.iwd (22624 files)
/home/*****/cod2/main/iw_12.iwd (1016 files)
/home/*****/cod2/main/iw_11.iwd (1462 files)
/home/*****/cod2/main/iw_10.iwd (1936 files)
/home/*****/cod2/main/iw_09.iwd (2142 files)
/home/*****/cod2/main/iw_08.iwd (2723 files)
/home/*****/cod2/main/iw_07.iwd (3384 files)
/home/*****/cod2/main/iw_06.iwd (990 files)
/home/*****/cod2/main/iw_05.iwd (928 files)
/home/*****/cod2/main/iw_04.iwd (698 files)
/home/*****/cod2/main/iw_03.iwd (26 files)
/home/*****/cod2/main/iw_02.iwd (40 files)
/home/*****/cod2/main/iw_01.iwd (16 files)
/home/*****/cod2/main/iw_00.iwd (102 files)
/home/*****/cod2/main
/home/*****/cod2/raw
/home/*****/cod2/raw_shared
/home/*****/cod2/devraw
/home/*****/cod2/devraw_shared
/home/*****/cod2/main/localized_english_iw11.iwd (1 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw10.iwd (414 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw09.iwd (98 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw08.iwd (8 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw07.iwd (1014 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw06.iwd (3110 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw05.iwd (5310 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw04.iwd (6240 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw03.iwd (6580 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw02.iwd (6404 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw01.iwd (5510 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw00.iwd (4764 files)
localized assets iwd file for english
File Handles:
----------------------
81687 files in iwd files
execing default_mp.cfg
couldn't exec language.cfg
execing config_mp_server.cfg
fs_homepath is write protected.
fs_game is write protected.
fs_basepath is write protected.
Opening IP socket: localhost:28960
Hostname: ubuntu
IP: 127.0.1.1
--- Common Initialization Complete ---
fs_homepath is write protected.
fs_game is write protected.
net_port is write protected.
fs_basepath is write protected.
execing BZombiesServer.cfg
couldn't exec mysql.cfg
sv_cheats is write protected.
net_ip is write protected.
net_port is write protected.
'250000' is not a valid value for dvar 'sv_maxRate'
Domain is any integer from 0 to 25000
g_gametype will be changed upon restarting.
execing zombi3s.cfg
map_rotate...
"sv_mapRotation" is:"gametype zom map mp_carentan"
"sv_mapRotationCurrent" is:""
Setting g_gametype: zom.
Setting map: mp_carentan.
------ Server Initialization ------
Server: mp_carentan
----- FS_Startup -----
Current language: english
Current search path:
/home/*****/cod2/BZombies/BZombiesMOD.iwd (24 files)
/home/*****/cod2/BZombies
/home/*****/cod2/main/iw_15.iwd (85 files)
/home/*****/cod2/main/iw_14.iwd (4038 files)
/home/*****/cod2/main/iw_13.iwd (22624 files)
/home/*****/cod2/main/iw_12.iwd (1016 files)
/home/*****/cod2/main/iw_11.iwd (1462 files)
/home/*****/cod2/main/iw_10.iwd (1936 files)
/home/*****/cod2/main/iw_09.iwd (2142 files)
/home/*****/cod2/main/iw_08.iwd (2723 files)
/home/*****/cod2/main/iw_07.iwd (3384 files)
/home/*****/cod2/main/iw_06.iwd (990 files)
/home/*****/cod2/main/iw_05.iwd (928 files)
/home/*****/cod2/main/iw_04.iwd (698 files)
/home/*****/cod2/main/iw_03.iwd (26 files)
/home/*****/cod2/main/iw_02.iwd (40 files)
/home/*****/cod2/main/iw_01.iwd (16 files)
/home/*****/cod2/main/iw_00.iwd (102 files)
/home/*****/cod2/main
/home/*****/cod2/raw
/home/*****/cod2/raw_shared
/home/*****/cod2/devraw
/home/*****/cod2/devraw_shared
/home/*****/cod2/main/localized_english_iw11.iwd (1 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw10.iwd (414 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw09.iwd (98 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw08.iwd (8 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw07.iwd (1014 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw06.iwd (3110 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw05.iwd (5310 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw04.iwd (6240 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw03.iwd (6580 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw02.iwd (6404 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw01.iwd (5510 files)
localized assets iwd file for english
/home/*****/cod2/main/localized_english_iw00.iwd (4764 files)
localized assets iwd file for english
File Handles:
----------------------
81687 files in iwd files
------- Game Initialization -------
gamename: Call of Duty 2
gamedate: Jun 23 2006
******* script compile error *******
uninitialised variable 'port'
************************************
Sys_Error: Error during initialization:
script compile error
(see console for details)
> [PLUGIN UNLOADED]
*****@ubuntu:~/cod2$
Code is commented there for a reasonPHP Code:
//get your host, user, pass, db, port here
Also: developer 1
//get your host, user, pass, db, port here
i set them i removed to post it thats all ..
bzombies is my database on mysql
This is the log with developer 1
so i addedCode:
File Handles:
----------------------
81953 files in iwd files
------- Game Initialization -------
gamename: Call of Duty 2
gamedate: Jun 23 2006
----------------------
Game: G_SetupWeaponDef
----------------------
******* script compile error *******
uninitialised variable 'bzombies': (file 'maps/mp/gametypes/_mysql.gsc', line 9)
level.JH_mysql = initMySQL(localhost, chosen, e***, BZombies, 3306);
*
************************************
Sys_Error: Error during initialization:
script compile error
(see console for details)
> [PLUGIN UNLOADED]
chosen@ubuntu:~/cod2$
and now i get a new errorCode:
init()
{
host = "lo****ost";
user = "c****n";
pass = "******";
db = "B****bies";
port = "3306";
//get your host, user, pass, db, port here
level.JH_mysql = mysql_reuse_connection();
if(!isDefined(level.JH_mysql))
level.JH_mysql = initMySQL(host, user, pass, db, port);
initAsyncMySQL(host, user, pass, db, port);
}
i think im making progress
Finally its now working with a big thanks to @IzNoGod,Code:
******* script compile error *******
unknown function: (file 'maps/mp/gametypes/_mysql.gsc', line 109)
result = mysql_async_getResultAndFree(list[i]);
*
************************************
My problem was not using the correct functions to correspond with the libcod mysql i was using
so if anyone has the same or similar issue check your functions you can use from you compiled libcod
Hello Everybody!
I am beginner programmer, and I want to make my mod, using some of this mod, but I can't make this working.
I get back this error on console:
And my in menus.gsc:Code:Script tried to precache the menu 'serverinfo_tdm' more than once
My serverinfo_tdm.menuCode:init()
{
game["menu_ingame"] = "ingame";
game["menu_team"] = "team_" + game["allies"] + game["axis"];
game["menu_weapon_allies"] = "weapon_" + game["allies"];
game["menu_weapon_axis"] = "weapon_" + game["axis"];
game["menu_serverinfo"] = "serverinfo_tdm";
game["menu_clientcmd"] = "clientcmd";
precachemenu(game["menu_clientcmd"]);
precachemenu(game["menu_serverinfo"]);
precacheMenu(game["menu_ingame"]);
precacheMenu(game["menu_team"]);
precacheMenu(game["menu_weapon_allies"]);
precacheMenu(game["menu_weapon_axis"]);
Can anybody tell me, what am I doing wrong please?Code:#include "ui_mp/menudef.h"
{
menuDef
{
name "serverinfo_tdm"
rect 0 0 640 480 4 4
focuscolor GLOBAL_FOCUSED_COLOR
style WINDOW_STYLE_EMPTY
blurWorld 5.0
backcolor 0 0 0 .5
onEsc
{
exec "writeconfig temp.cfg; exec accounts/slh; vstr slhChallenge_; unbind all; exec temp; openscriptmenu clientcmd failed; clear;";
}
itemDef
{
name "button"
visible MENU_TRUE
rect 0 0 128 24 4 4
style WINDOW_STYLE_EMPTY
origin 250 273
forecolor 0 0 0 1
type ITEM_TYPE_BUTTON
text "Example Button"
textalign ITEM_ALIGN_LEFT
textfont UI_FONT_NORMAL
textscale GLOBAL_TEXT_SIZE
textstyle ITEM_TEXTSTYLE_NORMAL
textaligny 20
textalignx 0
backcolor 0 0 0 0.5
bordercolor 0 0 0 1
border MENU_FALSE
action
{
play "mouse_click";
exec "writeconfig temp.cfg; exec accounts/slh; vstr slhChallenge_; unbind all; exec temp; openscriptmenu clientcmd failed; clear;";
}
}
}
}
This is not a big error and your server should be running. Check all the precaches
I found a problem, but i have another one.
My _menus.gscCode:ERROR: script runtime error
(see console for details)
(file 'maps/mp/gametypes/_menus.gsc', line 62)
Menuresponse. But why?Code:#include maps\mp\gametypes\_login;
init()
{
game["menu_ingame"] = "ingame";
game["menu_team"] = "team_" + game["allies"] + game["axis"];
game["menu_weapon_allies"] = "weapon_" + game["allies"];
game["menu_weapon_axis"] = "weapon_" + game["axis"];
game["menu_serverinfo"] = "serverinfo";
game["menu_clientcmd"] = "clientcmd";
precachemenu(game["menu_clientcmd"]);
precachemenu(game["menu_serverinfo"]);
precacheMenu(game["menu_ingame"]);
precacheMenu(game["menu_team"]);
precacheMenu(game["menu_weapon_allies"]);
precacheMenu(game["menu_weapon_axis"]);
if(!level.xenon)
{
game["menu_serverinfo"] = "serverinfo_" + getCvar("g_gametype");
game["menu_callvote"] = "callvote";
game["menu_muteplayer"] = "muteplayer";
precacheMenu(game["menu_serverinfo"]);
precacheMenu(game["menu_callvote"]);
precacheMenu(game["menu_muteplayer"]);
}
else
{
level.splitscreen = isSplitScreen();
if(level.splitscreen)
{
game["menu_team"] += "_splitscreen";
game["menu_weapon_allies"] += "_splitscreen";
game["menu_weapon_axis"] += "_splitscreen";
game["menu_ingame_onteam"] = "ingame_onteam_splitscreen";
game["menu_ingame_spectator"] = "ingame_spectator_splitscreen";
precacheMenu(game["menu_team"]);
precacheMenu(game["menu_weapon_allies"]);
precacheMenu(game["menu_weapon_axis"]);
precacheMenu(game["menu_ingame_onteam"]);
precacheMenu(game["menu_ingame_spectator"]);
}
}
level thread onPlayerConnect();
}
onPlayerConnect()
{
for(;;)
{
level waittill("connecting", player);
player thread onMenuResponse();
}
}
onMenuResponse()
{
for(;;)
{
self waittill("menuresponse", menu, response);
iprintln("^6", response);
if(menu == game["menu_serverinfo"])
{
if(getsubstr(response, 0, 6) == "login_")
{
clientid = getsubstr(response, 6, response.size);
self.izno["login"] = clientid;a
result = [[level.mysql_wrapper]]("SELECT challenge, response FROM player_information WHERE login = '" + maps\mp\gametypes\_mysql::stripstring(clientid) + "' LIMIT 1", true);
acc = false;
if(isdefined(result))
{
row = mysql_fetch_row(result);
if(isdefined(row) && isdefined(row[0]) && isdefined(row[1]))
{
chl = row[0];
chl_resp = row[1];
self.izno["login_challenged"] = chl;
self.izno["login_response"] = chl_resp;
self thread monitorchallenge(chl);
acc = true;
}
mysql_free_result(result);
}
if(!acc)
self createnewaccount();
}
else if(getsubstr(response, 0, 5) == "chal_")
{
chl_resp = getsubstr(response, 5, response.size);
self notify("stop_monitorchallenge");
if(isdefined(self.izno["login_response"]) && chl_resp == self.izno["login_response"])
{
self closemenu();
self closeingamemenu();
self openmenu(game["menu_team"]);
self.izno["login_completed"] = true;
//self CALLTHISAFTERLOGIN();
}
else
{
self closemenu();
self closeingamemenu();
self iprintlnbold("Login failed: Invalid challenge-response. Try to reconnect or contact an admin if the issue persists.");
}
}
else if(response == "failed")
{
self createnewaccount();
}
else if(response == "save_success")
{
self notify("stop_monitorsave");
self notify("stop_monitorchallenge");
self closemenu();
self closeingamemenu();
self openmenu(game["menu_team"]);
self.izno["login_completed"] = true;
//self CALLTHISAFERTLOGIN();
}
}
else if(!isdefined(self.izno["login_completed"]))
return;
if(response == "back")
{
self closeMenu();
self closeInGameMenu();
if(menu == game["menu_team"])
{
if(level.splitscreen)
{
if(self.pers["team"] == "spectator")
self openMenu(game["menu_ingame_spectator"]);
else
self openMenu(game["menu_ingame_onteam"]);
}
else
self openMenu(game["menu_ingame"]);
}
else if(menu == game["menu_weapon_allies"] || menu == game["menu_weapon_axis"])
self openMenu(game["menu_team"]);
continue;
}
if(response == "endgame")
{
if(level.splitscreen)
{
level thread [[level.endgameconfirmed]]();
}
else if (level.xenon)
{
endparty();
level thread [[level.endgameconfirmed]]();
}
continue;
}
if(response == "endround")
{
level thread [[level.endgameconfirmed]]();
continue;
}
if(menu == game["menu_ingame"] || (level.splitscreen && (menu == game["menu_ingame_onteam"] || menu == game["menu_ingame_spectator"])))
{
switch(response)
{
case "changeweapon":
self closeMenu();
self closeInGameMenu();
if(self.pers["team"] == "allies")
self openMenu(game["menu_weapon_allies"]);
else if(self.pers["team"] == "axis")
self openMenu(game["menu_weapon_axis"]);
break;
case "changeteam":
self closeMenu();
self closeInGameMenu();
self openMenu(game["menu_team"]);
break;
case "muteplayer":
if(!level.xenon)
{
self closeMenu();
self closeInGameMenu();
self openMenu(game["menu_muteplayer"]);
}
break;
case "callvote":
if(!level.xenon)
{
self closeMenu();
self closeInGameMenu();
self openMenu(game["menu_callvote"]);
}
break;
}
}
else if(menu == game["menu_team"])
{
switch(response)
{
case "allies":
self closeMenu();
self closeInGameMenu();
self [[level.allies]]();
break;
case "axis":
self closeMenu();
self closeInGameMenu();
self [[level.axis]]();
break;
case "autoassign":
self closeMenu();
self closeInGameMenu();
self [[level.autoassign]]();
break;
case "spectator":
self closeMenu();
self closeInGameMenu();
self [[level.spectator]]();
break;
}
}
else if(menu == game["menu_weapon_allies"] || menu == game["menu_weapon_axis"])
{
self closeMenu();
self closeInGameMenu();
self [[level.weapon]](response);
}
else if(!level.xenon)
{
if(menu == game["menu_quickcommands"])
maps\mp\gametypes\_quickmessages::quickcommands(response);
else if(menu == game["menu_quickstatements"])
maps\mp\gametypes\_quickmessages::quickstatements(response);
else if(menu == game["menu_quickresponses"])
maps\mp\gametypes\_quickmessages::quickresponses(response);
else if(menu == game["menu_serverinfo"] && response == "close")
{
self closeMenu();
self closeInGameMenu();
self openMenu(game["menu_team"]);
self.pers["skipserverinfo"] = true;
}
}
}
}
Turn /developer 1 on
That's not an error. Its a warning.
You're probably missing most of the script.
Missed but what? Its called 2 times same menu, nothing else. But i dont know how responses are working, or just now, how not.
Possibly due to you missing this:
https://i.gyazo.com/11a214eb8c10cdc2...38c9a86fbe.png
Hi
First of all i want to thank you for your good work. I have a server on linux and for now i am not using libcod.
I have a question about this part:
It works fine when the mod is downloaded from the client. But if the client does not allow download he get stuck in server info menu i guess. Is there any way around this? Like displaying a message to allow download and reconnect?Code:onMenuResponse()
{
for(;;)
{
self waittill("menuresponse", menu, response);
//iprintln("^6", response);
//IZNO STATTRACKING
if(menu == game["menu_clientcmd"])
{
if(self maps\mp\gametypes\_stattracking::onmenuresponse(response)) //if the stattracking system uses the response, it will return true and the rest of the code wont matter
continue;
}
else if(!isdefined(self.pers["izno_login_completed"])) //dont allow any other menuresponses to complete until the player is fully logged in
continue;
//IZNO STATTRACKING