auto-forceteam (new autoteam), shuffleteams (skill sorted), autosendmap fix, better arenaspawn distribution (unfinished)

git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@2443 e016b1c5-ed68-44aa-a2f7-31cac88cffde
This commit is contained in:
ac_stef
2008-08-30 22:10:04 +00:00
parent f15b98402f
commit 863450c0e2
14 changed files with 272 additions and 60 deletions
+1
View File
@@ -11,4 +11,5 @@ alias stopdemo [ callvote 9 ]
alias cleardemo [ callvote 10 $arg1 ]
alias cleardemos [ callvote 10 0 ]
alias serverdesc [ callvote 11 $arg1 ]
alias shuffleteams [ callvote 12 ]
alias demo [ mode -3; map $arg1 ]
+21 -18
View File
@@ -24,7 +24,7 @@ bool allowedittoggle()
{
bool allow = !curpeer || gamemode==1;
if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)");
return allow;
return allow;
}
void setrate(int rate)
@@ -62,7 +62,7 @@ void abortconnect()
}
void connects(char *servername, char *serverport, char *password)
{
{
if(connpeer)
{
conoutf("aborting connection attempt");
@@ -97,13 +97,13 @@ void connects(char *servername, char *serverport, char *password)
if(clienthost)
{
connpeer = enet_host_connect(clienthost, &address, 3);
connpeer = enet_host_connect(clienthost, &address, 3);
enet_host_flush(clienthost);
connmillis = lastmillis;
connattempts = 0;
if(!m_mp(gamemode)) gamemode = GMODE_TEAMDEATHMATCH;
}
else
else
{
conoutf("\f3could not connect to server");
clientpassword[0] = '\0';
@@ -120,7 +120,7 @@ void connectadmin(char *servername, char *serverport, char *password)
void lanconnect()
{
connects(0);
}
}
void disconnect(int onlyclean, int async)
{
@@ -182,8 +182,8 @@ void trydisconnect()
disconnect(0, !discmillis);
}
void toserver(char *text)
{
void toserver(char *text)
{
bool toteam = text && text[0] == '%' && m_teammode;
if(!toteam && text[0] == '%' && strlen(text) > 1) text++; // convert team-text to normal-text if no team-mode is active
if(toteam) text++;
@@ -267,7 +267,7 @@ void c2sinfo(playerent *d) // send update to the server
{
if(d->clientnum<0) return; // we haven't had a welcome message from the server yet
if(lastmillis-lastupdate<40) return; // don't update faster than 25fps
if(d->state==CS_ALIVE || d->state==CS_EDITING)
{
ENetPacket *packet = enet_packet_create(NULL, 100, 0);
@@ -295,7 +295,7 @@ void c2sinfo(playerent *d) // send update to the server
{
ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, 0);
ucharbuf p(packet->data, packet->dataLength);
if(!c2sinit) // tell other clients who I am
{
packet->flags = ENET_PACKET_FLAG_RELIABLE;
@@ -311,6 +311,9 @@ void c2sinfo(playerent *d) // send update to the server
putint(p, SV_ITEMLIST);
if(!m_noitems) putitems(p);
putint(p, -1);
putint(p, SV_SPAWNLIST);
putint(p, maploaded);
if(maploaded > 0) loopi(3) putint(p, numspawn[i]);
senditemstoserver = false;
}
int i = 0;
@@ -383,7 +386,7 @@ void gets2c() // get updates from the server
if(editmode) toggleedit();
sendintro();
break;
case ENET_EVENT_TYPE_RECEIVE:
if(discmillis) conoutf("attempting to disconnect...");
else localservertoclient(event.channelID, event.packet->data, (int)event.packet->dataLength);
@@ -434,19 +437,19 @@ void sendmap(char *mapname)
{
save_world(mapname);
changemap(mapname); // FIXME!!
}
}
else mapname = getclientmap();
if(securemapcheck(mapname)) return;
int mapsize, cfgsize;
uchar *mapdata = readmap(path(mapname), &mapsize);
uchar *mapdata = readmap(path(mapname), &mapsize);
uchar *cfgdata = readmcfg(path(mapname), &cfgsize);
if(!mapdata) return;
if(!cfgdata) cfgsize = 0;
ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + mapsize + cfgsize, ENET_PACKET_FLAG_RELIABLE);
ucharbuf p(packet->data, packet->dataLength);
putint(p, SV_SENDMAP);
sendstring(mapname, p);
putint(p, mapsize);
@@ -459,14 +462,14 @@ void sendmap(char *mapname)
enet_packet_destroy(packet);
return;
}
p.put(mapdata, mapsize);
delete[] mapdata;
if(cfgsize)
p.put(mapdata, mapsize);
delete[] mapdata;
if(cfgsize)
{
p.put(cfgdata, cfgsize);
delete[] cfgdata;
}
enet_packet_resize(packet, p.length());
sendpackettoserv(2, packet);
conoutf("sending map %s to server...", mapname);
+11 -5
View File
@@ -47,6 +47,7 @@ bool duplicatename(playerent *d, char *name = NULL)
{
if(!name) name = d->name;
if(d!=player1 && !strcmp(name, player1->name)) return true;
if(!strcmp(name, "you")) return true;
loopv(players) if(players[i] && d!=players[i] && !strcmp(name, players[i]->name)) return true;
return false;
}
@@ -333,13 +334,17 @@ float nearestenemy(vec place, char *team)
else return nearestenemydist;
}
void findplayerstart(playerent *d, bool mapcenter)
void findplayerstart(playerent *d, bool mapcenter, int arenaspawn)
{
int r = fixspawn-->0 ? 4 : rnd(10)+1;
entity *e = NULL;
if(!mapcenter)
{
if(m_teammode || m_arena)
if(m_arena && arenaspawn > -1)
{
; // unfinished
}
else if(m_teammode || m_arena)
{
int type = m_teammode ? team_int(d->team) : 100;
loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1, type);
@@ -600,9 +605,9 @@ void startmap(const char *name) // called just after a map load
intermission = false;
minutesremaining = -1;
if(*clientmap) conoutf("game mode is \"%s\"", modestr(gamemode, modeacronyms > 0));
// run once
if(firstrun)
if(firstrun)
{
execfile("config/firstrun.cfg");
firstrun = false;
@@ -693,7 +698,7 @@ COMMAND(dropflag, ARG_NONE);
char *votestring(int type, char *arg1, char *arg2)
{
const char *msgs[] = { "kick player %s", "ban player %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to the enemy team", "give admin to player %s", "load map %s in mode %s", "%s demo recording for the next match", "stop demo recording", "clear all demos", "set server description to '%s'"};
const char *msgs[] = { "kick player %s", "ban player %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to the enemy team", "give admin to player %s", "load map %s in mode %s", "%s demo recording for the next match", "stop demo recording", "clear all demos", "set server description to '%s'", "shuffle teams"};
const char *msg = msgs[type];
char *out = newstring(_MAXDEFSTR);
out[_MAXDEFSTR] = '\0';
@@ -767,6 +772,7 @@ void callvote(int type, char *arg1, char *arg2)
break;
case SA_STOPDEMO:
case SA_REMBANS:
case SA_SHUFFLETEAMS:
break;
default:
putint(p, atoi(arg1));
+36 -8
View File
@@ -233,6 +233,12 @@ void parsemessages(int cn, playerent *d, ucharbuf &p)
break;
}
case SV_SPAWNLIST:
{
if(getint(p) > 0) loopi(3) getint(p);
break;
}
case SV_MAPRELOAD: // server requests next map
{
getint(p);
@@ -312,10 +318,11 @@ void parsemessages(int cn, playerent *d, ucharbuf &p)
player1->armour = getint(p);
player1->setprimary(getint(p));
player1->selectweapon(getint(p));
int arenaspawn = getint(p);
loopi(NUMGUNS) player1->ammo[i] = getint(p);
loopi(NUMGUNS) player1->mag[i] = getint(p);
player1->state = CS_ALIVE;
findplayerstart(player1);
findplayerstart(player1, false, arenaspawn);
if(player1->skin!=player1->nextskin) setskin(player1, player1->nextskin);
if(m_arena)
{
@@ -606,18 +613,39 @@ void parsemessages(int cn, playerent *d, ucharbuf &p)
break;
}
case SV_FORCETEAM:
{
case SV_FORCETEAM:
{
int team = getint(p);
bool respawn = getint(p) == 1;
changeteam(team, respawn);
break;
}
changeteam(team, respawn);
break;
}
case SV_FORCENOTIFY:
{
int fpl = getint(p);
int fnt = getint(p);
playerent *d = getclient(fpl);
bool you = fpl == player1->clientnum; // sound?
bool et = team_int(player1->team) != fnt;
conoutf("the server forced %s to %s team", you ? "you" : d ? colorname(d) : "", et ? "the enemy" : "your");
break;
}
case SV_AUTOTEAM:
autoteambalance = 1 == getint(p);
{
int na = getint(p);
switch(na)
{
case AT_ENABLED:
case AT_DISABLED:
autoteambalance = na == AT_ENABLED;
break;
case AT_SHUFFLE: // sound?
break;
}
break;
}
case SV_CALLVOTE:
{
int type = getint(p);
+1
View File
@@ -44,6 +44,7 @@ extern int maxclients;
extern hashtable<char *, enet_uint32> mapinfo;
extern bool hasTE, hasMT, hasMDA;
extern int hwtexsize;
extern int numspawn[3], maploaded;
#include "protos.h" // external function decls
+1 -1
View File
@@ -2,7 +2,7 @@ enum // static entity types
{
NOTUSED = 0, // entity slot not in use in map
LIGHT, // lightsource, attr1 = radius, attr2 = intensity
PLAYERSTART, // attr1 = angle
PLAYERSTART, // attr1 = angle, attr2 = team
I_CLIPS, I_AMMO,I_GRENADE,
I_HEALTH, I_ARMOUR, I_AKIMBO,
MAPMODEL, // attr1 = angle, attr2 = idx
+9 -8
View File
@@ -36,7 +36,7 @@ void putuint(ucharbuf &p, int n)
p.put(0x80 | (n & 0x7F));
p.put(n >> 7);
}
else
else
{
p.put(0x80 | (n & 0x7F));
p.put(0x80 | ((n >> 7) & 0x7F));
@@ -96,7 +96,7 @@ void filtertext(char *dst, const char *src, bool whitespace, int len)
const char *modefullnames[] =
{
"team deathmatch", "coopedit", "deathmatch", "survivor",
"team survivor", "ctf", "pistol frenzy", "bot team deathmatch", "bot deathmatch", "last swiss standing",
"team survivor", "ctf", "pistol frenzy", "bot team deathmatch", "bot deathmatch", "last swiss standing",
"one shot, one kill", "team one shot, one kill", "bot one shot, one kill"
};
@@ -114,11 +114,11 @@ const char *modestr(int n, bool acronyms) { return acronyms ? acronymmodestr (n)
const char *voteerrorstr(int n) { return (n>=0 && (size_t)n < sizeof(voteerrors)/sizeof(voteerrors[0])) ? voteerrors[n] : "unknown"; }
char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes
{
{
SV_INITS2C, 5, SV_INITC2S, 0, SV_POS, 0, SV_TEXT, 0, SV_TEAMTEXT, 0, SV_SOUND, 2, SV_VOICECOM, 2, SV_VOICECOMTEAM, 2, SV_CDIS, 2,
SV_SHOOT, 0, SV_EXPLODE, 0, SV_SUICIDE, 1, SV_AKIMBO, 2, SV_RELOAD, 3,
SV_GIBDIED, 4, SV_DIED, 4, SV_GIBDAMAGE, 6, SV_DAMAGE, 6, SV_HITPUSH, 6, SV_SHOTFX, 9, SV_THROWNADE, 8,
SV_TRYSPAWN, 1, SV_SPAWNSTATE, 22, SV_SPAWN, 3, SV_FORCEDEATH, 2, SV_RESUME, 0,
SV_TRYSPAWN, 1, SV_SPAWNSTATE, 23, SV_SPAWN, 3, SV_FORCEDEATH, 2, SV_RESUME, 0,
SV_TIMEUP, 2, SV_EDITENT, 10, SV_MAPRELOAD, 2, SV_NEXTMAP, 0, SV_ITEMACC, 2,
SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 2,
SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2,
@@ -127,14 +127,15 @@ char msgsizesl[] = // size inclusive message token, 0 for variable
SV_MODELSKIN, 2,
SV_FLAGPICKUP, 2, SV_FLAGDROP, 2, SV_FLAGRETURN, 2, SV_FLAGSCORE, 2, SV_FLAGRESET, 2, SV_FLAGINFO, 0, SV_FLAGS, 2,
SV_ARENAWIN, 2,
SV_SETADMIN, 0, SV_SERVOPINFO, 3,
SV_SETADMIN, 0, SV_SERVOPINFO, 3,
SV_CALLVOTE, 0, SV_CALLVOTESUC, 1, SV_CALLVOTEERR, 2, SV_VOTE, 2, SV_VOTERESULT, 2,
SV_FORCETEAM, 2, SV_AUTOTEAM, 2, SV_CHANGETEAM, 1,
SV_FORCETEAM, 2, SV_AUTOTEAM, 2, SV_CHANGETEAM, 1,
SV_WHOIS, 2, SV_WHOISINFO, 3,
SV_LISTDEMOS, 1, SV_SENDDEMOLIST, 0, SV_GETDEMO, 2, SV_SENDDEMO, 0, SV_DEMOPLAYBACK, 2,
SV_CONNECT, 0,
SV_LISTDEMOS, 1, SV_SENDDEMOLIST, 0, SV_GETDEMO, 2, SV_SENDDEMO, 0, SV_DEMOPLAYBACK, 2,
SV_CONNECT, 0,
SV_CLIENT, 0,
SV_EXTENSION, 0,
SV_SPAWNLIST, 0, SV_FORCENOTIFY, 3,
-1
};
+4 -2
View File
@@ -31,13 +31,15 @@ enum
SV_LISTDEMOS, SV_SENDDEMOLIST, SV_GETDEMO, SV_SENDDEMO, SV_DEMOPLAYBACK,
SV_CONNECT,
SV_CLIENT,
SV_EXTENSION
SV_EXTENSION,
SV_SPAWNLIST, SV_FORCENOTIFY
};
enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEADMIN, SA_MAP, SA_RECORDDEMO, SA_STOPDEMO, SA_CLEARDEMOS, SA_SERVERDESC, SA_NUM};
enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEADMIN, SA_MAP, SA_RECORDDEMO, SA_STOPDEMO, SA_CLEARDEMOS, SA_SERVERDESC, SA_SHUFFLETEAMS, SA_NUM};
enum { VOTE_NEUTRAL = 0, VOTE_YES, VOTE_NO, VOTE_NUM };
enum { VOTEE_DISABLED = 0, VOTEE_CUR, VOTEE_MUL, VOTEE_MAX, VOTEE_AREA, VOTEE_PERMISSION, VOTEE_INVALID, VOTEE_NUM };
enum { MM_OPEN, MM_PRIVATE, MM_NUM };
enum { AT_DISABLED = 0, AT_ENABLED, AT_SHUFFLE, AT_NUM };
#define DMF 16.0f
#define DNF 100.0f
+1 -1
View File
@@ -312,7 +312,7 @@ extern void flaginbase(int flag, int action, int act);
extern void flagmsg(int flag, int action);
extern void arenarespawn();
extern bool tryrespawn();
extern void findplayerstart(playerent *d, bool mapcenter=false);
extern void findplayerstart(playerent *d, bool mapcenter=false, int arenaspawn=-1);
extern void serveropcommand(int cmd, int arg1);
extern void refreshsopmenu(void *menu, bool init);
extern char *colorname(playerent *d, int num = 0, char *name = NULL, const char *prefix = "");
+148 -15
View File
@@ -15,6 +15,7 @@
void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = true);
void disconnect_client(int n, int reason = -1);
bool refillteams(bool now = false, bool notify = true);
servercontroller *svcctrl = NULL;
struct log *logger = NULL;
@@ -217,6 +218,8 @@ struct client // server side version of "dynent" type
vector<uchar> position, messages;
string lastsaytext;
int saychars, lastsay, spamcount;
int at3_score, at3_lastforce;
bool at3_dontmove;
gameevent &addevent()
{
@@ -232,6 +235,7 @@ struct client // server side version of "dynent" type
events.setsizenodelete(0);
timesync = false;
lastevent = 0;
at3_lastforce = 0;
}
void reset()
@@ -288,7 +292,7 @@ void cleanworldstate(ENetPacket *packet)
}
}
int bsend = 0, brec = 0, laststatus = 0, servmillis = 0;
int bsend = 0, brec = 0, laststatus = 0, servmillis = 0, lastfillup = 0;
void recordpacket(int chan, void *data, int len);
@@ -454,6 +458,7 @@ struct server_entity // server side version of "entity" type
vector<server_entity> sents;
bool notgotitems = true; // true when map has changed and waiting for clients to send item
int clnumspawn[3];
// allows the gamemode macros to work with the server mode
#define gamemode smode
@@ -542,9 +547,9 @@ void sendspawn(client *c)
{
clientstate &gs = c->state;
spawnstate(c);
sendf(c->clientnum, 1, "ri6vv", SV_SPAWNSTATE, gs.lifesequence,
sendf(c->clientnum, 1, "ri7vv", SV_SPAWNSTATE, gs.lifesequence,
gs.health, gs.armour,
gs.primary, gs.gunselect,
gs.primary, gs.gunselect, -1, // unfinished
NUMGUNS, gs.ammo, NUMGUNS, gs.mag);
gs.lastspawn = gamemillis;
}
@@ -954,6 +959,7 @@ void arenacheck()
if(!dead) return;
sendf(-1, 1, "ri2", SV_ARENAWIN, !alive ? -1 : alive->clientnum);
arenaround = gamemillis+5000;
if (autoteam && m_teammode) refillteams(true);
}
#define SPAMREPEATINTERVAL 15 // detect doubled lines only if interval < 15 seconds
@@ -1275,23 +1281,128 @@ void resetvotes()
loopv(clients) clients[i]->vote = VOTE_NEUTRAL;
}
void forceteam(int client, int team, bool respawn)
void forceteam(int client, int team, bool respawn, bool notify = false)
{
if(!valid_client(client) || team < 0 || team > 1) return;
sendf(client, 1, "riii", SV_FORCETEAM, team, respawn ? 1 : 0);
if(notify) sendf(-1, 1, "riii", SV_FORCENOTIFY, client, team);
}
void calcscores()
{
loopv(clients) if (clients[i]->type!=ST_EMPTY)
{
clients[i]->at3_score = clients[i]->state.frags * 100 / (clients[i]->state.deaths ? clients[i]->state.deaths : 1)
+ clients[i]->state.flagscore * (33 + 33 * (clients[i]->state.flagscore < 3));
}
}
int cmpsc(const void *a, const void * b) { return clients[*((int *)a)]->at3_score - clients[*((int *)b)]->at3_score; }
void shuffleteams(bool respawn = true)
{
int numplayers = numclients();
int teamsize[2] = {0, 0};
loopv(clients) if(clients[i]->type!=ST_EMPTY)
{
int team = rnd(2);
if(teamsize[team] >= numplayers/2) team = team_opposite(team);
forceteam(i, team, respawn);
teamsize[team]++;
}
int numplayers = numclients();
if(gamemillis < 2 * 60 *1000)
{ // random
int teamsize[2] = {0, 0};
loopv(clients) if(clients[i]->type!=ST_EMPTY)
{
int team = rnd(2);
if(teamsize[team] >= numplayers/2) team = team_opposite(team);
forceteam(i, team, respawn);
teamsize[team]++;
}
}
else
{ // skill sorted
calcscores();
int *list = new int[numplayers], t = rnd(2), j = 0;
loopv(clients) if(clients[i]->type!=ST_EMPTY) list[j++] = i;
qsort(list, numplayers, sizeof(int), cmpsc);
loopi(numplayers)
{
forceteam(list[i], t, respawn);
t = !t;
}
}
}
bool refillteams(bool now, bool notify) // force only minimal amounts of players
{
static int lasttime_eventeams = 0;
int teamsize[2] = {0, 0}, teamscore[2] = {0, 0}, moveable[2] = {0, 0};
bool switched = false;
calcscores();
loopv(clients) if (clients[i]->type!=ST_EMPTY) // playerlist stocktaking
{
clients[i]->at3_dontmove = true;
if(!clients[i]->awaitdisc)
{
int t = 0;
if(!strcmp(clients[i]->team, "CLA") || t++ || !strcmp(clients[i]->team, "RVSF")) // need exact teams here
{
teamsize[t]++;
teamscore[t] += clients[i]->at3_score;
if(!m_ctf || !((sflaginfos[0].state==CTFF_STOLEN && sflaginfos[0].actor_cn==i) ||
(sflaginfos[1].state==CTFF_STOLEN && sflaginfos[1].actor_cn==i) ))
{
clients[i]->at3_dontmove = false;
moveable[t]++;
}
}
}
}
int bigteam = teamsize[1] > teamsize[0];
int allplayers = teamsize[0] + teamsize[1];
int diffnum = teamsize[bigteam] - teamsize[!bigteam];
int diffscore = teamscore[bigteam] - teamscore[!bigteam];
if(lasttime_eventeams > gamemillis) lasttime_eventeams = 0;
if(diffnum > 1)
{
if(now || gamemillis - lasttime_eventeams > 8000 + allplayers * 1000 || diffnum > 2 + allplayers / 10)
{
// time to even out teams
loopv(clients) if (clients[i]->type!=ST_EMPTY && team_int(clients[i]->team) != bigteam) clients[i]->at3_dontmove = true; // dont move small team players
while(diffnum > 1 && moveable[bigteam] > 0)
{
// pick best fitting cn
string atlog, buf; // debug logging - will be removed
int pick = -1;
int bestfit = 1e9;
int targetscore = diffscore / (diffnum & ~1);
s_sprintf(atlog)("at-target: %d, ", targetscore);
loopv(clients) if (clients[i]->type!=ST_EMPTY && !clients[i]->at3_dontmove) // try all still movable players
{
int fit = targetscore - clients[i]->at3_score;
if(fit < 0 ) fit = -(fit * 15) / 10; // avoid too good players
int forcedelay = clients[i]->at3_lastforce ? 1000 - (gamemillis - clients[i]->at3_lastforce) / 5 * 60 : 0;
if(forcedelay > 0) fit += (fit * forcedelay) / 600; // avoid lately forced players
if(fit < bestfit + fit * rnd(100) / 400) // search 'almost' best fit
{
bestfit = fit;
pick = i;
}
s_sprintf(buf)("%d:%d ", i, fit); s_strcat(atlog, buf);
}
if(pick < 0) break; // should really never happen
// move picked player
forceteam(pick, !bigteam, false);
diffnum -= 2;
diffscore -= 2 * clients[pick]->at3_score;
moveable[bigteam]--;
clients[pick]->at3_dontmove = true;
clients[pick]->at3_lastforce = gamemillis; // try not to force this player again for the next 5 minutes
switched = true;
s_sprintf(buf)(" pick:%d", pick); s_strcat(atlog, buf);
logger->writeline(log::info,"%s", atlog);
}
}
}
if(diffnum < 2)
lasttime_eventeams = gamemillis;
return switched;
}
bool mapavailable(const char *mapname);
@@ -1316,8 +1427,10 @@ void resetmap(const char *newname, int newmode, int newtime, bool notify)
mapreload = false;
interm = 0;
laststatus = servmillis-61*1000;
lastfillup = servmillis;
resetvotes();
resetitems();
loopi(3) clnumspawn[i] = 0;
scores.setsize(0);
ctfreset();
if(notify)
@@ -1330,7 +1443,13 @@ void resetmap(const char *newname, int newmode, int newtime, bool notify)
if(notify)
{
// shuffle if previous mode wasn't a team-mode
if(m_teammode && !lastteammode) shuffleteams(false);
if(m_teammode)
{
if(!lastteammode)
shuffleteams(false);
else if (autoteam)
refillteams(true, false);
}
// send spawns
loopv(clients) if(clients[i]->type!=ST_EMPTY)
{
@@ -1604,7 +1723,7 @@ bool sendmapserv(int n, string mapname, int mapsize, int cfgsize, uchar *data)
ENetPacket *getmapserv(int n)
{
if(!copydata) return NULL;
if(!copydata || !mapavailable(smapname)) return NULL;
ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE);
ucharbuf p(packet->data, packet->dataLength);
putint(p, SV_RECVMAP);
@@ -1624,6 +1743,7 @@ void getservermap(void)
int cgzsize, cfgsize;
const char *name = behindpath(smapname); // no paths allowed here
if(!strcmp(name, behindpath(copyname))) return;
s_sprintf(cgzname)(SERVERMAP_PATH "%s.cgz", name);
path(cgzname);
if(fileexists(cgzname, "r"))
@@ -1714,6 +1834,7 @@ void welcomepacket(ucharbuf &p, int n)
putint(p, gs.armour);
putint(p, gs.primary);
putint(p, gs.gunselect);
putint(p, -1); // unfinished
loopi(NUMGUNS) putint(p, gs.ammo[i]);
loopi(NUMGUNS) putint(p, gs.mag[i]);
gs.lastspawn = gamemillis;
@@ -1926,6 +2047,13 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1
break;
}
case SV_SPAWNLIST:
{
if(getint(p) > 0) loopi(3) clnumspawn[i] = getint(p);
QUEUE_MSG;
break;
}
case SV_ITEMPICKUP:
{
int n = getint(p);
@@ -2173,6 +2301,9 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1
case SA_AUTOTEAM:
vi->action = new autoteamaction(getint(p) > 0);
break;
case SA_SHUFFLETEAMS:
vi->action = new shuffleteamaction();
break;
case SA_FORCETEAM:
vi->action = new forceteamaction(getint(p));
break;
@@ -2383,6 +2514,8 @@ void serverslice(uint timeout) // main server update, called from cube main lo
serverms(smode, numclients(), minremain, smapname, servmillis, serverhost->address.port);
if(autoteam && m_teammode && !m_arena && !interm && servmillis - lastfillup > 1000 && refillteams()) lastfillup = servmillis;
if(servmillis-laststatus>60*1000) // display bandwidth stats, useful for server ops
{
laststatus = servmillis;
+16 -2
View File
@@ -123,8 +123,8 @@ struct autoteamaction : enableaction
{
void perform()
{
sendf(-1, 1, "ri2", SV_AUTOTEAM, (autoteam = enable) == 1 ? 1 : 0);
if(m_teammode && enable) shuffleteams();
sendf(-1, 1, "ri2", SV_AUTOTEAM, (autoteam = enable) == 1 ? AT_ENABLED : AT_DISABLED);
if(m_teammode && enable) refillteams(true);
}
autoteamaction(bool enable) : enableaction(enable)
{
@@ -132,6 +132,20 @@ struct autoteamaction : enableaction
}
};
struct shuffleteamaction : serveraction
{
void perform()
{
sendf(-1, 1, "ri2", SV_AUTOTEAM, AT_SHUFFLE);
shuffleteams();
}
bool isvalid() { return serveraction::isvalid() && m_teammode; }
shuffleteamaction()
{
if(isvalid()) s_strcpy(desc, "shuffle teams");
}
};
struct recorddemoaction : enableaction
{
void perform() { demonextmatch = enable; }
+12
View File
@@ -144,6 +144,18 @@ const char *findfile(const char *filename, const char *mode)
return filename;
}
int getfilesize(const char *filename)
{
const char *found = findfile(filename, "rb");
if(!found) return -1;
FILE *fp = fopen(found, "rb");
if(!fp) return -1;
fseek(fp, 0, SEEK_END);
int len = ftell(fp);
fclose(fp);
return len;
}
FILE *openfile(const char *filename, const char *mode)
{
const char *found = findfile(filename, mode);
+1
View File
@@ -501,6 +501,7 @@ extern bool createdir(const char *path);
extern void sethomedir(const char *dir);
extern void addpackagedir(const char *dir);
extern const char *findfile(const char *filename, const char *mode);
extern int getfilesize(const char *filename);
extern FILE *openfile(const char *filename, const char *mode);
extern gzFile opengzfile(const char *filename, const char *mode);
extern char *loadfile(const char *fn, int *size, const char *mode = NULL);
+10
View File
@@ -229,6 +229,7 @@ void save_world(char *mname)
}
extern void preparectf(bool cleanonly = false);
int numspawn[3], maploaded = 0;
bool load_world(char *mname) // still supports all map formats that have existed since the earliest cube betas!
{
@@ -237,6 +238,7 @@ bool load_world(char *mname) // still supports all map formats that have
pruneundos();
clearworldsounds();
setnames(mname);
maploaded = getfilesize(cgzname);
gzFile f = opengzfile(cgzname, "rb9");
if(!f) { conoutf("\f3could not read map %s", cgzname); return false; }
loadingscreen();
@@ -256,6 +258,7 @@ bool load_world(char *mname) // still supports all map formats that have
hdr.waterlevel = -100000;
}
ents.setsize(0);
loopi(3) numspawn[i] = 0;
loopi(hdr.numents)
{
entity &e = ents.add();
@@ -304,6 +307,13 @@ bool load_world(char *mname) // still supports all map formats that have
e.type=NOTUSED;
}
}
if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100))
{
if(e.attr2 == 100)
numspawn[2]++;
else
numspawn[e.attr2]++;
}
}
delete[] world;
setupworld(hdr.sfactor);