void renderscore(dynent *d) { sprintf_sd(lag)("%d", d->plag); sprintf_sd(name) ("(%s)", d->name); sprintf_s(scorelines.add().s)("%d\t%s\t%d\t%s\t%s", d->frags, d->state==CS_LAGGED ? "LAG" : lag, d->ping, d->team, d->state==CS_DEAD ? name : d->name); menumanual(0, scorelines.length()-1, scorelines.last().s); };
void delayedload(md2 *m) { if (!m->loaded) { sprintf_sd(name1)("packages/models/%s/tris.md2", m->loadname); if (!m->load(path(name1))) fatal("loadmodel: ", name1); sprintf_sd(name2)("packages/models/%s/skin.jpg", m->loadname); int xs, ys; ogl::installtex(FIRSTMDL+m->mdlnum, path(name2), xs, ys); m->loaded = true; } }
bool render(void) { if(vmenu<0) { menustack.setsize(0); return false; } if(vmenu==1) refreshservers(); gmenu &m = menus[vmenu]; sprintf_sd(title)(vmenu>1 ? "[ %s menu ]" : "%s", m.name); int mdisp = m.items.length(); int w = 0; loopi(mdisp) { int x = text_width(m.items[i].text); if(x>w) w = x; } int tw = text_width(title); if(tw>w) w = tw; int step = FONTH/4*5; int h = (mdisp+2)*step; int y = (VIRTH-h)/2; int x = (VIRTW-w)/2; blendbox(x-FONTH/2*3, y-FONTH, x+w+FONTH/2*3, y+h+FONTH, true); draw_text(title, x, y,2); y += FONTH*2; if(vmenu) { int bh = y+m.menusel*step; blendbox(x-FONTH, bh-10, x+w+FONTH, bh+FONTH+10, false); } loopj(mdisp) { draw_text(m.items[j].text, x, y, 2); y += step; } return true; }
void renderscores() { if(!scoreson) return; scorelines.setsize(0); if(!demoplayback) renderscore(player1); loopv(players) if(players[i]) renderscore(players[i]); // Added by Rick: Render Score for bots loopv(bots) if(bots[i]) renderscore(bots[i]); sortmenu(0, scorelines.length()); if(m_teammode) { teamsused = 0; loopv(players) addteamscore(players[i]); // Added by Rick: Add team scores for bots loopv(bots) addteamscore(bots[i]); if(!demoplayback) addteamscore(player1); teamscores[0] = 0; loopj(teamsused) { sprintf_sd(sc)("[ %s: %d ]", teamname[j], teamscore[j]); strcat_s(teamscores, sc); }; menumanual(0, scorelines.length(), ""); menumanual(0, scorelines.length()+1, teamscores); }; };
void sendmap(char *mapname) { if(*mapname) save_world(mapname); changemap(mapname); mapname = getclientmap(); int mapsize; uchar *mapdata = readmap(mapname, &mapsize); if(!mapdata) return; ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE); uchar *start = packet->data; uchar *p = start+2; putint(p, SV_SENDMAP); sendstring(mapname, p); putint(p, mapsize); if(65535 - (p - start) < mapsize) { conoutf("map %s is too large to send", mapname); free(mapdata); enet_packet_destroy(packet); return; }; memcpy(p, mapdata, mapsize); p += mapsize; free(mapdata); *(ushort *)start = ENET_HOST_TO_NET_16(p-start); enet_packet_resize(packet, p-start); sendpackettoserv(packet); conoutf("sending map %s to server...", mapname); sprintf_sd(msg)("[map %s uploaded to server, \"getmap\" to receive it]", mapname); toserver(msg); }
void savegame(char *name) { if(!m_classicsp) { conoutf("can only save classic sp games"); return; }; sprintf_sd(fn)("savegames/%s.csgz", name); savestate(fn); stop(); conoutf("wrote %s", fn); };
u8 *retrieveservers(u8 *buf, int buflen) { sprintf_sd(path)("%sretrieve.do?item=list", masterpath); httpgetsend(masterserver, masterbase, path, "cubeserver", "Cube Server"); ENetBuffer eb; buf[0] = 0; eb.data = buf; eb.dataLength = buflen-1; while (mssock!=ENET_SOCKET_NULL) httpgetrecieve(eb); return stripheader(buf); }
void updatemasterserver(int seconds) { if (seconds>updmaster) { // send alive signal to masterserver every hour of uptime sprintf_sd(path)("%sregister.do?action=add", masterpath); httpgetsend(masterserver, masterbase, path, "cubeserver", "Cube Server"); masterrep[0] = 0; masterb.data = masterrep; masterb.dataLength = MAXTRANS-1; updmaster = seconds+60*60; } }
void record(char *name) { if(m_sp) { conoutf("cannot record singleplayer games"); return; }; int cn = getclientnum(); if(cn<0) return; sprintf_sd(fn)("demos/%s.cdgz", name); savestate(fn); gzputi(cn); conoutf("started recording demo to %s", fn); demorecording = true; starttime = lastmillis; ddamage = bdamage = 0; };
bool vote(char *map, int reqmode, int sender) { strcpy_s(clients[sender].mapvote, map); clients[sender].modevote = reqmode; int yes = 0, no = 0; loopv(clients) if (clients[i].type!=ST_EMPTY) { if (clients[i].mapvote[0]) { if (strcmp(clients[i].mapvote, map)==0 && clients[i].modevote==reqmode) yes++; else no++; } else no++; } if (yes==1 && no==0) return true; // single player sprintf_sd(msg)("%s suggests %s on map %s (set map to vote)", clients[sender].name, game::modestr(reqmode), map); sendservmsg(msg); if (yes/(float)(yes+no) <= 0.5f) return false; sendservmsg("vote passed"); resetvotes(); return true; }
int lookuptexture(int tex, int &xs, int &ys) { int frame = 0; // other frames? int tid = mapping[tex][frame]; if(tid>=FIRSTTEX) { xs = texx[tid-FIRSTTEX]; ys = texy[tid-FIRSTTEX]; return tid; }; xs = ys = 16; if(!tid) return 1; // crosshair :) loopi(curtex) // lazily happens once per "texture" command, basically { if(strcmp(mapname[tex][frame], texname[i])==0) { mapping[tex][frame] = tid = i+FIRSTTEX; xs = texx[i]; ys = texy[i]; return tid; }; }; if(curtex==MAXTEX) fatal("loaded too many textures"); int tnum = curtex+FIRSTTEX; strcpy_s(texname[curtex], mapname[tex][frame]); sprintf_sd(name)("packages%c%s", PATHDIV, texname[curtex]); if(installtex(tnum, name, xs, ys)) { mapping[tex][frame] = tnum; texx[curtex] = xs; texy[curtex] = ys; curtex++; return tnum; } else { return mapping[tex][frame] = FIRSTTEX; // temp fix }; };
void httpgetsend(ENetAddress &ad, const char *hostname, const char *req, const char *ref, const char *agent) { if (ad.host==ENET_HOST_ANY) { printf("looking up %s...\n", hostname); enet_address_set_host(&ad, hostname); if (ad.host==ENET_HOST_ANY) return; } if (mssock!=ENET_SOCKET_NULL) enet_socket_destroy(mssock); mssock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, NULL); if (mssock==ENET_SOCKET_NULL) { printf("could not open socket\n"); return; } if (enet_socket_connect(mssock, &ad)<0) { printf("could not connect\n"); return; } ENetBuffer buf; sprintf_sd(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent); buf.data = httpget; buf.dataLength = strlen((char *)buf.data); printf("sending request to %s...\n", hostname); enet_socket_send(mssock, NULL, &buf, 1); }
bool render(void) { if (vmenu<0) { menustack.setsize(0); return false; } // zoom in the menu const auto scr = vec2f(rr::VIRTW, rr::VIRTH); ogl::pushmode(ogl::PROJECTION); ogl::setortho(0.f, scr.x, 0.f, scr.y, -1.f, 1.f); text::displaywidth(10.f); if (vmenu==1) browser::refreshservers(); auto &m = menus[vmenu]; sprintf_sd(title)(vmenu>1 ? "- %s menu -" : "%s", m.name); const auto mdisp = m.items.length(); auto w = 0.f; loopi(mdisp) w = max(w, text::width(m.items[i].text)); w = max(w, text::width(title)); const auto fh = text::displaydim().y; const auto step = fh*5.f/4.f; const auto h = (float(mdisp)+2.f)*step; const auto x = (scr.x-w)/2.f; auto y = (scr.y+h)/2.f; rr::blendbox(x-fh/2.f*3.f, y-h, x+w+fh/2.f*3.f, y+2.f*fh, true); OGL(VertexAttrib4f,ogl::ATTRIB_COL,1.f,1.f,1.f,1.f); text::draw(title,x,y); y -= fh*2.f; if (vmenu) { const auto bh = y-m.menusel*step; rr::blendbox(x-fh, bh-fh*0.1f, x+w+fh, bh+fh*1.1f, false); } OGL(VertexAttrib4f,ogl::ATTRIB_COL,1.f,1.f,1.f,1.f); loopj(mdisp) { text::draw(m.items[j].text, x, y); y -= step; } ogl::popmode(ogl::PROJECTION); return true; }
void localservertoclient(uchar *buf, int len) // processes any updates from the server { if(ENET_NET_TO_HOST_16(*(ushort *)buf)!=len) neterr("packet length"); incomingdemodata(buf, len); uchar *end = buf+len; uchar *p = buf+2; char text[MAXTRANS]; int cn = -1, type; dynent *d = NULL; bool mapchanged = false; while(p<end) switch(type = getint(p)) { case SV_INITS2C: // welcome messsage from the server { cn = getint(p); int prot = getint(p); if(prot!=PROTOCOL_VERSION) { conoutf("you are using a different game protocol (you: %d, server: %d)", PROTOCOL_VERSION, prot); disconnect(); return; }; toservermap[0] = 0; clientnum = cn; // we are now fully connected if(!getint(p)) strcpy_s(toservermap, getclientmap()); // we are the first client on this server, set map sgetstr(); if(text[0] && strcmp(text, clientpassword)) { conoutf("you need to set the correct password to join this server!"); disconnect(); return; }; if(getint(p)==1) { conoutf("server is FULL, disconnecting.."); }; break; }; case SV_POS: // position of another client { cn = getint(p); d = getclient(cn); if(!d) return; d->o.x = getint(p)/DMF; d->o.y = getint(p)/DMF; d->o.z = getint(p)/DMF; d->yaw = getint(p)/DAF; d->pitch = getint(p)/DAF; d->roll = getint(p)/DAF; d->vel.x = getint(p)/DVF; d->vel.y = getint(p)/DVF; d->vel.z = getint(p)/DVF; int f = getint(p); d->strafe = (f&3)==3 ? -1 : f&3; f >>= 2; d->move = (f&3)==3 ? -1 : f&3; d->onfloor = (f>>2)&1; int state = f>>3; if(state==CS_DEAD && d->state!=CS_DEAD) d->lastaction = lastmillis; d->state = state; if(!demoplayback) updatepos(d); break; }; case SV_SOUND: playsound(getint(p), &d->o); break; case SV_TEXT: sgetstr(); conoutf("%s:\f %s", d->name, text); break; case SV_MAPCHANGE: sgetstr(); changemapserv(text, getint(p)); mapchanged = true; break; case SV_ITEMLIST: { int n; if(mapchanged) { senditemstoserver = false; resetspawns(); }; while((n = getint(p))!=-1) if(mapchanged) setspawn(n, true); break; }; case SV_MAPRELOAD: // server requests next map { getint(p); sprintf_sd(nextmapalias)("nextmap_%s", getclientmap()); char *map = getalias(nextmapalias); // look up map in the cycle changemap(map ? map : getclientmap()); break; }; case SV_INITC2S: // another client either connected or changed name/team { sgetstr(); if(d->name[0]) // already connected { if(strcmp(d->name, text)) conoutf("%s is now known as %s", d->name, text); } else // new client { c2sinit = false; // send new players my info again conoutf("connected: %s", text); }; strcpy_s(d->name, text); sgetstr(); strcpy_s(d->team, text); d->lifesequence = getint(p); break; }; case SV_CDIS: cn = getint(p); if(!(d = getclient(cn))) break; conoutf("player %s disconnected", d->name[0] ? d->name : "[incompatible client]"); zapdynent(players[cn]); break; case SV_SHOT: { int gun = getint(p); vec s, e; s.x = getint(p)/DMF; s.y = getint(p)/DMF; s.z = getint(p)/DMF; e.x = getint(p)/DMF; e.y = getint(p)/DMF; e.z = getint(p)/DMF; if(gun==GUN_SG) createrays(s, e); shootv(gun, s, e, d); break; }; case SV_DAMAGE: { int target = getint(p); int damage = getint(p); int ls = getint(p); if(target==clientnum) { if(ls==player1->lifesequence) selfdamage(damage, cn, d); } else playsound(S_PAIN1+rnd(5), &getclient(target)->o); break; }; case SV_DIED: { int actor = getint(p); if(actor==cn) { conoutf("%s suicided", d->name); } else if(actor==clientnum) { int frags; if(isteam(player1->team, d->team)) { frags = -1; conoutf("you fragged a teammate (%s)", d->name); } else { frags = 1; conoutf("you fragged %s", d->name); }; addmsg(1, 2, SV_FRAGS, player1->frags += frags); } else { dynent *a = getclient(actor); if(a) { if(isteam(a->team, d->name)) { conoutf("%s fragged his teammate (%s)", a->name, d->name); } else { conoutf("%s fragged %s", a->name, d->name); }; }; }; playsound(S_DIE1+rnd(2), &d->o); d->lifesequence++; break; }; case SV_FRAGS: players[cn]->frags = getint(p); break; case SV_ITEMPICKUP: setspawn(getint(p), false); getint(p); break; case SV_ITEMSPAWN: { uint i = getint(p); setspawn(i, true); if(i>=(uint)ents.length()) break; vec v = { ents[i].x, ents[i].y, ents[i].z }; playsound(S_ITEMSPAWN, &v); break; }; case SV_ITEMACC: // server acknowledges that I picked up this item realpickup(getint(p), player1); break; case SV_PING: getint(p); break; case SV_PONG: addmsg(0, 2, SV_CLIENTPING, player1->ping = (player1->ping*5+lastmillis-getint(p))/6); break; case SV_CLIENTPING: players[cn]->ping = getint(p); break; case SV_GAMEMODE: nextmode = getint(p); break; case SV_TIMEUP: timeupdate(getint(p)); break; case SV_RECVMAP: { sgetstr(); conoutf("received map \"%s\" from server, reloading..", text); int mapsize = getint(p); writemap(text, mapsize, p); p += mapsize; changemapserv(text, gamemode); break; }; case SV_SERVMSG: sgetstr(); conoutf("%s", text); break; case SV_EXT: // so we can messages without breaking previous clients/servers, if necessary { for(int n = getint(p); n; n--) getint(p); break; }; default: neterr("type"); return; }; };
void loadgame(char *name) { sprintf_sd(fn)("savegames/%s.csgz", name); loadstate(fn); };
void demo(char *name) { sprintf_sd(fn)("demos/%s.cdgz", name); loadstate(fn); demoloading = true; };
int execute(char *p, bool isdown) // all evaluation happens here, recursively { const int MAXWORDS = 25; // limit, remove char *w[MAXWORDS]; int val = 0; for(bool cont = true; cont;) // for each ; seperated statement { int numargs = MAXWORDS; loopi(MAXWORDS) // collect all argument values { w[i] = (char*) ""; if(i>numargs) continue; char *s = parseword(p); // parse and evaluate exps if(!s) { numargs = i; s = (char*) ""; }; if(*s=='$') s = lookup(s); // substitute variables w[i] = s; }; p += strcspn(p, ";\n\0"); cont = *p++!=0; // more statements if this isn't the end of the string const char *c = w[0]; if(*c=='/') c++; // strip irc-style command prefix if(!*c) continue; // empty statement ident *id = idents->access(c); if(!id) { val = ATOI(c); if(!val && *c!='0') console::out("unknown command: %s", c); } else switch(id->type) { case ID_COMMAND: // game defined commands switch(id->narg) // use very ad-hoc function signature, and just call it { case ARG_1INT: if(isdown) ((void (__cdecl *)(int))id->fun)(ATOI(w[1])); break; case ARG_2INT: if(isdown) ((void (__cdecl *)(int, int))id->fun)(ATOI(w[1]), ATOI(w[2])); break; case ARG_3INT: if(isdown) ((void (__cdecl *)(int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3])); break; case ARG_4INT: if(isdown) ((void (__cdecl *)(int, int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3]), ATOI(w[4])); break; case ARG_NONE: if(isdown) ((void (__cdecl *)())id->fun)(); break; case ARG_1STR: if(isdown) ((void (__cdecl *)(char *))id->fun)(w[1]); break; case ARG_2STR: if(isdown) ((void (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; case ARG_3STR: if(isdown) ((void (__cdecl *)(char *, char *, char*))id->fun)(w[1], w[2], w[3]); break; case ARG_5STR: if(isdown) ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break; case ARG_DOWN: ((void (__cdecl *)(bool))id->fun)(isdown); break; case ARG_DWN1: ((void (__cdecl *)(bool, char *))id->fun)(isdown, w[1]); break; case ARG_1EXP: if(isdown) val = ((int (__cdecl *)(int))id->fun)(execute(w[1])); break; case ARG_2EXP: if(isdown) val = ((int (__cdecl *)(int, int))id->fun)(execute(w[1]), execute(w[2])); break; case ARG_1EST: if(isdown) val = ((int (__cdecl *)(char *))id->fun)(w[1]); break; case ARG_2EST: if(isdown) val = ((int (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; case ARG_VARI: if(isdown) { string r; // limit, remove r[0] = 0; for(int i = 1; i<numargs; i++) { strcat_s(r, w[i]); // make string-list out of all arguments if(i==numargs-1) break; strcat_s(r, " "); }; ((void (__cdecl *)(char *))id->fun)(r); break; } }; break; case ID_VAR: // game defined variabled if(isdown) { if(!w[1][0]) console::out("%s = %d", c, *id->storage); // var with no value just prints its current value else { if(id->min>id->max) { console::out("variable is read-only"); } else { int i1 = ATOI(w[1]); if(i1<id->min || i1>id->max) { i1 = i1<id->min ? id->min : id->max; // clamp to valid range console::out("valid range for %s is %d..%d", c, id->min, id->max); } *id->storage = i1; }; if(id->fun) ((void (__cdecl *)())id->fun)(); // call trigger function if available }; }; break; case ID_ALIAS: // alias, also used as functions and (global) variables for(int i = 1; i<numargs; i++) { sprintf_sd(t)("arg%d", i); // set any arguments as (global) arg values so functions can access them alias(t, w[i]); }; char *action = newstring(id->action); // create new string here because alias could rebind itself val = execute(action, isdown); gp()->deallocstr(action); break; }; loopj(numargs) gp()->deallocstr(w[j]); }; return val; };
void draw_textf(char *fstr, int left, int top, int gl_num, int arg) { sprintf_sd(str)(fstr,arg); draw_text(str, left, top, gl_num); };