Index: src/Cube.mm ================================================================== --- src/Cube.mm +++ src/Cube.mm @@ -83,12 +83,12 @@ }; SDL_FreeSurface(image); }; } -COMMAND(screenshot, ARG_NONE); -COMMAND(quit, ARG_NONE); +COMMAND(screenshot, ARG_NONE) +COMMAND(quit, ARG_NONE) void keyrepeat(bool on) { SDL_EnableKeyRepeat( @@ -213,12 +213,12 @@ execfile("data/defaults.cfg"); exec("autoexec.cfg"); log("localconnect"); localconnect(); - changemap( - "metl3"); // if this map is changed, also change depthcorrect() + // if this map is changed, also change depthcorrect() + changemap(@"metl3"); log("mainloop"); int ignore = 5; for (;;) { int millis = SDL_GetTicks() * gamespeed / 100; Index: src/client.mm ================================================================== --- src/client.mm +++ src/client.mm @@ -68,12 +68,12 @@ { c2sinit = false; strn0cpy(player1->team, name, 5); } -COMMANDN(team, newteam, ARG_1STR); -COMMANDN(name, newname, ARG_1STR); +COMMANDN(team, newteam, ARG_1CSTR) +COMMANDN(name, newname, ARG_1CSTR) void writeclientinfo(FILE *f) { fprintf(f, "name \"%s\"\nteam \"%s\"\n", player1->name, player1->team); @@ -169,14 +169,14 @@ echo(char *text) { conoutf(@"%s", text); } -COMMAND(echo, ARG_VARI); -COMMANDN(say, toserver, ARG_VARI); -COMMANDN(connect, connects, ARG_1STR); -COMMANDN(disconnect, trydisconnect, ARG_NONE); +COMMAND(echo, ARG_VARI) +COMMANDN(say, toserver, ARG_VARI) +COMMANDN(connect, connects, ARG_1CSTR) +COMMANDN(disconnect, trydisconnect, ARG_NONE) // collect c2s messages conveniently vector messages; @@ -210,21 +210,21 @@ conoutf(@"server network error, disconnecting..."); disconnect(); } int lastupdate = 0, lastping = 0; -string toservermap; +OFString *toservermap; bool senditemstoserver = false; // after a map change, since server doesn't have map data string clientpassword; void password(char *p) { strcpy_s(clientpassword, p); -}; -COMMAND(password, ARG_1STR); +} +COMMAND(password, ARG_1CSTR) bool netmapstart() { senditemstoserver = true; @@ -233,15 +233,15 @@ void initclientnet() { ctext[0] = 0; - toservermap[0] = 0; + toservermap = @""; clientpassword[0] = 0; newname("unnamed"); newteam("red"); -}; +} void sendpackettoserv(void *packet) { if (clienthost) { @@ -252,98 +252,107 @@ } void c2sinfo(dynent *d) // send update to the server { - if (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 - ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, 0); - uchar *start = packet->data; - uchar *p = start + 2; - bool serveriteminitdone = false; - if (toservermap[0]) // suggest server to change map - { // do this exclusively as map change may invalidate rest of update - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_MAPCHANGE); - sendstring(toservermap, p); - toservermap[0] = 0; - putint(p, nextmode); - } else { - putint(p, SV_POS); - putint(p, clientnum); - putint( - p, (int)(d->o.x * DMF)); // quantize coordinates to 1/16th - // of a cube, between 1 and 3 bytes - putint(p, (int)(d->o.y * DMF)); - putint(p, (int)(d->o.z * DMF)); - putint(p, (int)(d->yaw * DAF)); - putint(p, (int)(d->pitch * DAF)); - putint(p, (int)(d->roll * DAF)); - putint( - p, (int)(d->vel.x * - DVF)); // quantize to 1/100, almost always 1 byte - putint(p, (int)(d->vel.y * DVF)); - putint(p, (int)(d->vel.z * DVF)); - // pack rest in 1 byte: strafe:2, move:2, onfloor:1, state:3 - putint(p, (d->strafe & 3) | ((d->move & 3) << 2) | - (((int)d->onfloor) << 4) | - ((editmode ? CS_EDITING : d->state) << 5)); - - if (senditemstoserver) { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_ITEMLIST); - if (!m_noitems) - putitems(p); - putint(p, -1); - senditemstoserver = false; - serveriteminitdone = true; - }; - if (ctext[0]) // player chat, not flood protected for now - { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_TEXT); - sendstring(ctext, p); - ctext[0] = 0; - }; - if (!c2sinit) // tell other clients who I am - { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - c2sinit = true; - putint(p, SV_INITC2S); - sendstring(player1->name, p); - sendstring(player1->team, p); - putint(p, player1->lifesequence); - }; - loopv(messages) // send messages collected during the previous - // frames - { - ivector &msg = messages[i]; - if (msg[1]) - packet->flags = ENET_PACKET_FLAG_RELIABLE; - loopi(msg[0]) putint(p, msg[i + 2]); - }; - messages.setsize(0); - if (lastmillis - lastping > 250) { - putint(p, SV_PING); - putint(p, lastmillis); - lastping = lastmillis; - }; - }; - *(ushort *)start = ENET_HOST_TO_NET_16(p - start); - enet_packet_resize(packet, p - start); - incomingdemodata(start, p - start, true); - if (clienthost) { - enet_host_broadcast(clienthost, 0, packet); - enet_host_flush(clienthost); - } else - localclienttoserver(packet); - lastupdate = lastmillis; - if (serveriteminitdone) - loadgamerest(); // hack -}; + @autoreleasepool { + if (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 + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, 0); + uchar *start = packet->data; + uchar *p = start + 2; + bool serveriteminitdone = false; + if (toservermap.length > 0) // suggest server to change map + { // do this exclusively as map change may invalidate rest of + // update + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_MAPCHANGE); + sendstring(toservermap.UTF8String, p); + toservermap = @""; + putint(p, nextmode); + } else { + putint(p, SV_POS); + putint(p, clientnum); + putint( + p, (int)(d->o.x * + DMF)); // quantize coordinates to 1/16th + // of a cube, between 1 and 3 bytes + putint(p, (int)(d->o.y * DMF)); + putint(p, (int)(d->o.z * DMF)); + putint(p, (int)(d->yaw * DAF)); + putint(p, (int)(d->pitch * DAF)); + putint(p, (int)(d->roll * DAF)); + putint( + p, (int)(d->vel.x * DVF)); // quantize to 1/100, + // almost always 1 byte + putint(p, (int)(d->vel.y * DVF)); + putint(p, (int)(d->vel.z * DVF)); + // pack rest in 1 byte: strafe:2, move:2, onfloor:1, + // state:3 + putint( + p, (d->strafe & 3) | ((d->move & 3) << 2) | + (((int)d->onfloor) << 4) | + ((editmode ? CS_EDITING : d->state) << 5)); + + if (senditemstoserver) { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_ITEMLIST); + if (!m_noitems) + putitems(p); + putint(p, -1); + senditemstoserver = false; + serveriteminitdone = true; + }; + if (ctext[0]) // player chat, not flood protected for + // now + { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_TEXT); + sendstring(ctext, p); + ctext[0] = 0; + }; + if (!c2sinit) // tell other clients who I am + { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + c2sinit = true; + putint(p, SV_INITC2S); + sendstring(player1->name, p); + sendstring(player1->team, p); + putint(p, player1->lifesequence); + }; + loopv(messages) // send messages collected during the + // previous frames + { + ivector &msg = messages[i]; + if (msg[1]) + packet->flags = + ENET_PACKET_FLAG_RELIABLE; + loopi(msg[0]) putint(p, msg[i + 2]); + }; + messages.setsize(0); + if (lastmillis - lastping > 250) { + putint(p, SV_PING); + putint(p, lastmillis); + lastping = lastmillis; + }; + }; + *(ushort *)start = ENET_HOST_TO_NET_16(p - start); + enet_packet_resize(packet, p - start); + incomingdemodata(start, p - start, true); + if (clienthost) { + enet_host_broadcast(clienthost, 0, packet); + enet_host_flush(clienthost); + } else + localclienttoserver(packet); + lastupdate = lastmillis; + if (serveriteminitdone) + loadgamerest(); // hack + } +} void gets2c() // get updates from the server { ENetEvent event; Index: src/clientextras.mm ================================================================== --- src/clientextras.mm +++ src/clientextras.mm @@ -159,43 +159,46 @@ }; // sendmap/getmap commands, should be replaced by more intuitive map downloading 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); +sendmap(const char *mapname) +{ + @autoreleasepool { + if (*mapname) + save_world(mapname); + changemap(@(mapname)); + mapname = getclientmap().UTF8String; + 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 getmap() { @@ -208,7 +211,7 @@ enet_packet_resize(packet, p - start); sendpackettoserv(packet); conoutf(@"requesting map from server..."); } -COMMAND(sendmap, ARG_1STR); -COMMAND(getmap, ARG_NONE); +COMMAND(sendmap, ARG_1CSTR) +COMMAND(getmap, ARG_NONE) Index: src/clientgame.mm ================================================================== --- src/clientgame.mm +++ src/clientgame.mm @@ -8,11 +8,11 @@ void mode(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); }; -COMMAND(mode, ARG_1INT); +COMMAND(mode, ARG_1INT) bool intermission = false; dynent *player1 = newdynent(); // our client dvector players; // other clients @@ -21,19 +21,19 @@ VARP(sensitivityscale, 1, 1, 10000); VARP(invmouse, 0, 0, 1); int lastmillis = 0; int curtime = 10; -string clientmap; +OFString *clientmap; extern int framesinmap; -char * +OFString * getclientmap() { return clientmap; -}; +} void resetmovement(dynent *d) { d->k_left = false; @@ -218,29 +218,29 @@ if (player1->state == CS_DEAD) { player1->attacking = false; if (m_arena) { conoutf(@"waiting for new round to start..."); return; - }; + } if (m_sp) { nextmode = gamemode; changemap(clientmap); return; - }; // if we die in SP we try the same map again + } // if we die in SP we try the same map again respawnself(); - }; -}; + } +} int sleepwait = 0; string sleepcmd; void sleepf(char *msec, char *cmd) { sleepwait = atoi(msec) + lastmillis; strcpy_s(sleepcmd, cmd); }; -COMMANDN(sleep, sleepf, ARG_2STR); +COMMANDN(sleep, sleepf, ARG_2STR) void updateworld(int millis) // main game update loop { if (lastmillis) { @@ -359,17 +359,17 @@ { if (!intermission && (player1->jumpnext = on)) respawn(); }; -COMMAND(backward, ARG_DOWN); -COMMAND(forward, ARG_DOWN); -COMMAND(left, ARG_DOWN); -COMMAND(right, ARG_DOWN); -COMMANDN(jump, jumpn, ARG_DOWN); -COMMAND(attack, ARG_DOWN); -COMMAND(showscores, ARG_DOWN); +COMMAND(backward, ARG_DOWN) +COMMAND(forward, ARG_DOWN) +COMMAND(left, ARG_DOWN) +COMMAND(right, ARG_DOWN) +COMMANDN(jump, jumpn, ARG_DOWN) +COMMAND(attack, ARG_DOWN) +COMMAND(showscores, ARG_DOWN) void fixplayer1range() { const float MAXPITCH = 90.0f; @@ -482,13 +482,13 @@ } void initclient() { - clientmap[0] = 0; + clientmap = @""; initclientnet(); -}; +} void startmap(char *name) // called just after a map load { if (netmapstart() && m_sp) { @@ -501,18 +501,20 @@ spawncycle = -1; spawnplayer(player1); player1->frags = 0; loopv(players) if (players[i]) players[i]->frags = 0; resetspawns(); - strcpy_s(clientmap, name); + @autoreleasepool { + clientmap = @(name); + } if (editmode) toggleedit(); setvar("gamespeed", 100); setvar("fog", 180); setvar("fogcolour", 0x8099B3); showscores(false); intermission = false; framesinmap = 0; conoutf(@"game mode is %s", modestr(gamemode)); -}; +} -COMMANDN(map, changemap, ARG_1STR); +COMMANDN(map, changemap, ARG_1STR) Index: src/clients2c.mm ================================================================== --- src/clients2c.mm +++ src/clients2c.mm @@ -2,32 +2,32 @@ #include "cube.h" extern int clientnum; extern bool c2sinit, senditemstoserver; -extern string toservermap; +extern OFString *toservermap; extern string clientpassword; void neterr(char *s) { conoutf(@"illegal network message (%s)", s); disconnect(); -}; +} void changemapserv(char *name, int mode) // forced map change from the server { gamemode = mode; load_world(name); -}; +} void -changemap(char *name) // request map change, server may ignore +changemap(OFString *name) // request map change, server may ignore { - strcpy_s(toservermap, name); -}; + toservermap = name; +} // update the position of other clients in the game in our world // don't care if he's in the scenery or other players, // just don't overlap with our client @@ -80,16 +80,16 @@ @"protocol (you: %d, server: %d)", PROTOCOL_VERSION, prot); disconnect(); return; }; - toservermap[0] = 0; + toservermap = @""; clientnum = cn; // we are now fully connected if (!getint(p)) - strcpy_s(toservermap, - getclientmap()); // we are the first client - // on this server, set map + // we are the first client on this server, set + // map + toservermap = getclientmap(); sgetstr(); if (text[0] && strcmp(text, clientpassword)) { conoutf(@"you need to set the correct password " @"to join this server!"); disconnect(); @@ -158,14 +158,15 @@ } case SV_MAPRELOAD: // server requests next map { getint(p); - sprintf_sd(nextmapalias)("nextmap_%s", getclientmap()); - char *map = + OFString *nextmapalias = [OFString + stringWithFormat:@"nextmap_%@", getclientmap()]; + OFString *map = getalias(nextmapalias); // look up map in the cycle - changemap(map ? map : getclientmap()); + changemap(map != nil ? map : getclientmap()); break; } case SV_INITC2S: // another client either connected or changed // name/team Index: src/command.mm ================================================================== --- src/command.mm +++ src/command.mm @@ -5,16 +5,16 @@ enum { ID_VAR, ID_COMMAND, ID_ALIAS }; @interface Ident : OFObject @property (nonatomic) int type; // one of ID_* above -@property (nonatomic) char *name; -@property (nonatomic) int min, max; // ID_VAR -@property (nonatomic) int *storage; // ID_VAR -@property (nonatomic) void (*fun)(); // ID_VAR, ID_COMMAND -@property (nonatomic) int narg; // ID_VAR, ID_COMMAND -@property (nonatomic) char *action; // ID_ALIAS +@property (copy, nonatomic) OFString *name; +@property (nonatomic) int min, max; // ID_VAR +@property (nonatomic) int *storage; // ID_VAR +@property (nonatomic) void (*fun)(); // ID_VAR, ID_COMMAND +@property (nonatomic) int narg; // ID_VAR, ID_COMMAND +@property (copy, nonatomic) OFString *action; // ID_ALIAS @property (nonatomic) bool persist; @end @implementation Ident @end @@ -24,11 +24,11 @@ { sprintf_s(s)("%d", i); } char * -exchangestr(char *o, char *n) +exchangestr(char *o, const char *n) { gp()->deallocstr(o); return newstring(n); } @@ -42,18 +42,18 @@ Ident *b = idents[@(name)]; if (!b) { Ident *b = [[Ident alloc] init]; b.type = ID_ALIAS; - b.name = newstring(name); - b.action = newstring(action); + b.name = @(name); + b.action = @(action); b.persist = true; - idents[@(name)] = b; + idents[b.name] = b; } else { if (b.type == ID_ALIAS) - b.action = exchangestr(b.action, action); + b.action = @(action); else conoutf( @"cannot redefine builtin %s with an alias", name); } @@ -70,18 +70,18 @@ idents = [[OFMutableDictionary alloc] init]; @autoreleasepool { Ident *v = [[Ident alloc] init]; v.type = ID_VAR; - v.name = name; + v.name = @(name); v.min = min; v.max = max; v.storage = storage; v.fun = fun; v.persist = persist; - idents[@(name)] = v; + idents[v.name] = v; } return cur; } @@ -107,21 +107,19 @@ @autoreleasepool { return (idents[@(name)] != nil); } } -char * -getalias(char *name) +OFString * +getalias(OFString *name) { - @autoreleasepool { - Ident *i = idents[@(name)]; - return i != nil && i.type == ID_ALIAS ? i.action : NULL; - } + Ident *i = idents[name]; + return i != nil && i.type == ID_ALIAS ? i.action : nil; } bool -addcommand(char *name, void (*fun)(), int narg) +addcommand(OFString *name, void (*fun)(), int narg) { if (idents == nil) idents = [[OFMutableDictionary alloc] init]; @autoreleasepool { @@ -129,11 +127,11 @@ c.type = ID_COMMAND; c.name = name; c.fun = fun; c.narg = narg; - idents[@(name)] = c; + idents[name] = c; } return false; } @@ -204,11 +202,11 @@ case ID_VAR: string t; itoa(t, *(ID.storage)); return exchangestr(n, t); case ID_ALIAS: - return exchangestr(n, ID.action); + return exchangestr(n, ID.action.UTF8String); } } } conoutf(@"unknown alias lookup: %s", n + 1); @@ -296,11 +294,11 @@ case ARG_NONE: if (isdown) ((void(__cdecl *)()) ID.fun)(); break; - case ARG_1STR: + case ARG_1CSTR: if (isdown) ((void(__cdecl *)( char *))ID.fun)( w[1]); break; @@ -383,10 +381,21 @@ } ((void(__cdecl *)( char *))ID.fun)(r); break; } + case ARG_1STR: + if (isdown) { + @autoreleasepool { + ((void( + __cdecl *)( + OFString *)) + ID.fun)( + @(w[1])); + } + } + break; } break; // game defined variables case ID_VAR: @@ -476,11 +485,12 @@ sprintf_sd(t)("arg%d", i); alias(t, w[i]); } // create new string here because alias // could rebind itself - char *action = newstring(ID.action); + char *action = + newstring(ID.action.UTF8String); val = execute(action, isdown); gp()->deallocstr(action); break; } } @@ -517,14 +527,14 @@ completeidx = 0; } __block int idx = 0; [idents enumerateKeysAndObjectsUsingBlock:^( OFString *name, Ident *ident, bool *stop) { - if (strncmp(ident.name, s + 1, completesize) == 0 && + if (strncmp(ident.name.UTF8String, s + 1, completesize) == 0 && idx++ == completeidx) { strcpy_s(s, "/"); - strcat_s(s, ident.name); + strcat_s(s, ident.name.UTF8String); } }]; completeidx++; if (completeidx >= idx) completeidx = 0; @@ -563,27 +573,29 @@ writeclientinfo(f); fprintf(f, "\n"); [idents enumerateKeysAndObjectsUsingBlock:^( OFString *name, Ident *ident, bool *stop) { if (ident.type == ID_VAR && ident.persist) { - fprintf(f, "%s %d\n", ident.name, *ident.storage); + fprintf(f, "%s %d\n", ident.name.UTF8String, + *ident.storage); } }]; fprintf(f, "\n"); writebinds(f); fprintf(f, "\n"); [idents enumerateKeysAndObjectsUsingBlock:^( OFString *name, Ident *ident, bool *stop) { - if (ident.type == ID_ALIAS && !strstr(ident.name, "nextmap_")) { - fprintf( - f, "alias \"%s\" [%s]\n", ident.name, ident.action); + if (ident.type == ID_ALIAS && + !strstr(ident.name.UTF8String, "nextmap_")) { + fprintf(f, "alias \"%s\" [%s]\n", ident.name.UTF8String, + ident.action.UTF8String); } }]; fclose(f); } -COMMAND(writecfg, ARG_NONE); +COMMAND(writecfg, ARG_NONE) // below the commands that implement a small imperative language. thanks to the // semantics of // () and [] expressions, any control construct can be defined trivially. @@ -660,84 +672,91 @@ loopi(n) s += strspn(s += strcspn(s, " \0"), " "); s[strcspn(s, " \0")] = 0; concat(s); } -COMMANDN(loop, loopa, ARG_2STR); -COMMANDN(while, whilea, ARG_2STR); -COMMANDN(if, ifthen, ARG_3STR); -COMMAND(onrelease, ARG_DWN1); -COMMAND(exec, ARG_1STR); -COMMAND(concat, ARG_VARI); -COMMAND(concatword, ARG_VARI); -COMMAND(at, ARG_2STR); -COMMAND(listlen, ARG_1EST); +COMMANDN(loop, loopa, ARG_2STR) +COMMANDN(while, whilea, ARG_2STR) +COMMANDN(if, ifthen, ARG_3STR) +COMMAND(onrelease, ARG_DWN1) +COMMAND(exec, ARG_1CSTR) +COMMAND(concat, ARG_VARI) +COMMAND(concatword, ARG_VARI) +COMMAND(at, ARG_2STR) +COMMAND(listlen, ARG_1EST) int add(int a, int b) { return a + b; } -COMMANDN(+, add, ARG_2EXP); +COMMANDN(+, add, ARG_2EXP) + int mul(int a, int b) { return a * b; } -COMMANDN(*, mul, ARG_2EXP); +COMMANDN(*, mul, ARG_2EXP) + int sub(int a, int b) { return a - b; } -COMMANDN(-, sub, ARG_2EXP); +COMMANDN(-, sub, ARG_2EXP) + int divi(int a, int b) { return b ? a / b : 0; } -COMMANDN(div, divi, ARG_2EXP); +COMMANDN(div, divi, ARG_2EXP) + int mod(int a, int b) { return b ? a % b : 0; } -COMMAND(mod, ARG_2EXP); +COMMAND(mod, ARG_2EXP) + int equal(int a, int b) { return (int)(a == b); } -COMMANDN(=, equal, ARG_2EXP); +COMMANDN(=, equal, ARG_2EXP) + int lt(int a, int b) { return (int)(a < b); } -COMMANDN(<, lt, ARG_2EXP); +COMMANDN(<, lt, ARG_2EXP) + int gt(int a, int b) { return (int)(a > b); } -COMMANDN(>, gt, ARG_2EXP); +COMMANDN(>, gt, ARG_2EXP) int strcmpa(char *a, char *b) { return strcmp(a, b) == 0; } -COMMANDN(strcmp, strcmpa, ARG_2EST); +COMMANDN(strcmp, strcmpa, ARG_2EST) int rndn(int a) { return a > 0 ? rnd(a) : 0; } -COMMANDN(rnd, rndn, ARG_1EXP); +COMMANDN(rnd, rndn, ARG_1EXP) int explastmillis() { return lastmillis; } -COMMANDN(millis, explastmillis, ARG_1EXP); +COMMANDN(millis, explastmillis, ARG_1EXP) Index: src/console.mm ================================================================== --- src/console.mm +++ src/console.mm @@ -20,13 +20,12 @@ setconskip(int n) { conskip += n; if (conskip < 0) conskip = 0; -}; - -COMMANDN(conskip, setconskip, ARG_1INT); +} +COMMANDN(conskip, setconskip, ARG_1INT) void conline(const char *sf, bool highlight) // add a line to the console buffer { cline cl; @@ -98,13 +97,12 @@ keymap(char *code, char *key, char *action) { keyms[numkm].code = atoi(code); keyms[numkm].name = newstring(key); keyms[numkm++].action = newstringbuf(action); -}; - -COMMAND(keymap, ARG_3STR); +} +COMMAND(keymap, ARG_3STR) void bindkey(char *key, char *action) { for (char *x = key; *x; x++) @@ -113,13 +111,12 @@ { strcpy_s(keyms[i].action, action); return; }; conoutf(@"unknown key \"%s\"", key); -}; - -COMMANDN(bind, bindkey, ARG_2STR); +} +COMMANDN(bind, bindkey, ARG_2STR) void saycommand(char *init) // turns input to the command line on or off { SDL_EnableUNICODE(saycommandon = (init != NULL)); @@ -132,14 +129,14 @@ void mapmsg(char *s) { strn0cpy(hdr.maptitle, s, 128); -}; +} -COMMAND(saycommand, ARG_VARI); -COMMAND(mapmsg, ARG_1STR); +COMMAND(saycommand, ARG_VARI) +COMMAND(mapmsg, ARG_1CSTR) #ifndef _WIN32 # include # include #endif @@ -196,13 +193,12 @@ if (!rec && n >= 0 && n < vhistory.length()) { rec = true; execute(vhistory[vhistory.length() - n - 1]); rec = false; }; -}; - -COMMAND(history, ARG_1INT); +} +COMMAND(history, ARG_1INT) void keypress(int code, bool isdown, int cooked) { if (saycommandon) // keystrokes go to commandline Index: src/cube.h ================================================================== --- src/cube.h +++ src/cube.h @@ -377,30 +377,31 @@ ARG_1INT, ARG_2INT, ARG_3INT, ARG_4INT, ARG_NONE, - ARG_1STR, + ARG_1CSTR, ARG_2STR, ARG_3STR, ARG_5STR, ARG_DOWN, ARG_DWN1, ARG_1EXP, ARG_2EXP, ARG_1EST, ARG_2EST, - ARG_VARI + ARG_VARI, + ARG_1STR }; // nasty macros for registering script functions, abuses globals to avoid // excessive infrastructure #define COMMANDN(name, fun, nargs) \ OF_CONSTRUCTOR() \ { \ enqueueInit(^{ \ - addcommand(#name, (void (*)())fun, nargs); \ + addcommand(@ #name, (void (*)())fun, nargs); \ }); \ } #define COMMAND(name, nargs) COMMANDN(name, name, nargs) #define VARP(name, min, cur, max) \ int name; \ Index: src/editing.mm ================================================================== --- src/editing.mm +++ src/editing.mm @@ -63,13 +63,12 @@ projreset(); }; keyrepeat(editmode); selset = false; editing = editmode; -}; - -COMMANDN(edittoggle, toggleedit, ARG_NONE); +} +COMMANDN(edittoggle, toggleedit, ARG_NONE) void correctsel() // ensures above invariant { selset = !OUTBORD(sel.x, sel.y); @@ -353,13 +352,12 @@ { EDITSEL; bool isfloor = flr == 0; editheightxy(isfloor, amount, sel); addmsg(1, 7, SV_EDITH, sel.x, sel.y, sel.xs, sel.ys, isfloor, amount); -}; - -COMMAND(editheight, ARG_2INT); +} +COMMAND(editheight, ARG_2INT) void edittexxy(int type, int t, block &sel) { loopselxy(switch (type) { @@ -449,25 +447,26 @@ void heightfield(int t) { edittype(t == 0 ? FHF : CHF); -}; +} +COMMAND(heightfield, ARG_1INT) + void solid(int t) { edittype(t == 0 ? SPACE : SOLID); -}; +} +COMMAND(solid, ARG_1INT) + void corner() { edittype(CORNER); -}; - -COMMAND(heightfield, ARG_1INT); -COMMAND(solid, ARG_1INT); -COMMAND(corner, ARG_NONE); +} +COMMAND(corner, ARG_NONE) void editequalisexy(bool isfloor, block &sel) { int low = 127, hi = -128; @@ -492,13 +491,12 @@ { bool isfloor = flr == 0; EDITSEL; editequalisexy(isfloor, sel); addmsg(1, 6, SV_EDITE, sel.x, sel.y, sel.xs, sel.ys, isfloor); -}; - -COMMAND(equalize, ARG_1INT); +} +COMMAND(equalize, ARG_1INT) void setvdeltaxy(int delta, block &sel) { loopselxy(s->vdelta = max(s->vdelta + delta, 0)); @@ -600,18 +598,18 @@ EDITSEL; newentity(sel.x, sel.y, (int)player1->o.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4)); }; -COMMANDN(select, selectpos, ARG_4INT); -COMMAND(edittag, ARG_1INT); -COMMAND(replace, ARG_NONE); -COMMAND(archvertex, ARG_3INT); -COMMAND(arch, ARG_2INT); -COMMAND(slope, ARG_2INT); -COMMANDN(vdelta, setvdelta, ARG_1INT); -COMMANDN(undo, editundo, ARG_NONE); -COMMAND(copy, ARG_NONE); -COMMAND(paste, ARG_NONE); -COMMAND(edittex, ARG_2INT); -COMMAND(newent, ARG_5STR); -COMMAND(perlin, ARG_3INT); +COMMANDN(select, selectpos, ARG_4INT) +COMMAND(edittag, ARG_1INT) +COMMAND(replace, ARG_NONE) +COMMAND(archvertex, ARG_3INT) +COMMAND(arch, ARG_2INT) +COMMAND(slope, ARG_2INT) +COMMANDN(vdelta, setvdelta, ARG_1INT) +COMMANDN(undo, editundo, ARG_NONE) +COMMAND(copy, ARG_NONE) +COMMAND(paste, ARG_NONE) +COMMAND(edittex, ARG_2INT) +COMMAND(newent, ARG_5STR) +COMMAND(perlin, ARG_3INT) Index: src/init.mm ================================================================== --- src/init.mm +++ src/init.mm @@ -1,24 +1,24 @@ #include #import "cube.h" #import "protos.h" -static OFMutableArray *queue; +static std::vector *queue; void enqueueInit(void (^init)(void)) { - if (queue == nil) - queue = [[OFMutableArray alloc] init]; + if (queue == NULL) + queue = new std::vector(); - [queue addObject:init]; + queue->push_back(init); } void processInitQueue(void) { - for (void (^init)(void) in queue) + for (auto &init : *queue) init(); - [queue removeAllObjects]; + queue->clear(); } Index: src/menus.mm ================================================================== --- src/menus.mm +++ src/menus.mm @@ -125,15 +125,15 @@ { gmenu &menu = menus.last(); mitem &mi = menu.items.add(); mi.text = newstring(text); mi.action = action[0] ? newstring(action) : mi.text; -}; +} +COMMAND(menuitem, ARG_2STR) -COMMAND(menuitem, ARG_2STR); -COMMAND(showmenu, ARG_1STR); -COMMAND(newmenu, ARG_1STR); +COMMAND(showmenu, ARG_1CSTR) +COMMAND(newmenu, ARG_1CSTR) bool menukey(int code, bool isdown) { if (vmenu <= 0) Index: src/protos.h ================================================================== --- src/protos.h +++ src/protos.h @@ -4,18 +4,18 @@ extern int variable(char *name, int min, int cur, int max, int *storage, void (*fun)(), bool persist); extern void setvar(char *name, int i); extern int getvar(char *name); extern bool identexists(char *name); -extern bool addcommand(char *name, void (*fun)(), int narg); +extern bool addcommand(OFString *name, void (*fun)(), int narg); extern int execute(char *p, bool down = true); extern void exec(char *cfgfile); extern bool execfile(char *cfgfile); extern void resetcomplete(); extern void complete(char *s); extern void alias(char *name, char *action); -extern char *getalias(char *name); +extern OFString *getalias(OFString *name); extern void writecfg(); // console extern void keypress(int code, bool isdown, int cooked); extern void renderconsole(); @@ -88,16 +88,16 @@ // clientgame extern void mousemove(int dx, int dy); extern void updateworld(int millis); extern void startmap(char *name); -extern void changemap(char *name); +extern void changemap(OFString *name); extern void initclient(); extern void spawnplayer(dynent *d); extern void selfdamage(int damage, int actor, dynent *act); extern dynent *newdynent(); -extern char *getclientmap(); +extern OFString *getclientmap(); extern const char *modestr(int n); extern void zapdynent(dynent *&d); extern dynent *getclient(int cn); extern void timeupdate(int timeremain); extern void resetmovement(dynent *d); @@ -180,14 +180,14 @@ extern void particle_splash(int type, int num, int fade, vec &p); extern void particle_trail(int type, int fade, vec &from, vec &to); extern void render_particles(int time); // worldio -extern void save_world(char *fname); +extern void save_world(const char *fname); extern void load_world(char *mname); extern void writemap(char *mname, int msize, uchar *mdata); -extern uchar *readmap(char *mname, int *msize); +extern uchar *readmap(const char *mname, int *msize); extern void loadgamerest(); extern void incomingdemodata(uchar *buf, int len, bool extras = false); extern void demoplaybackstep(); extern void stop(); extern void stopifrecording(); Index: src/rendercubes.mm ================================================================== --- src/rendercubes.mm +++ src/rendercubes.mm @@ -64,19 +64,19 @@ void showmip() { showm = !showm; -}; +} +COMMAND(showmip, ARG_NONE) + void mipstats(int a, int b, int c) { if (showm) conoutf(@"1x1/2x2/4x4: %d / %d / %d", a, b, c); -}; - -COMMAND(showmip, ARG_NONE); +} #define stripend() \ { \ if (floorstrip || deltastrip) { \ addstrip(ogltex, firstindex, curvert - firstindex); \ Index: src/renderextras.mm ================================================================== --- src/renderextras.mm +++ src/renderextras.mm @@ -211,13 +211,12 @@ int xs, ys; if (!installtex(texnum + i, path(name), xs, ys, true)) conoutf(@"could not load sky textures"); }; strcpy_s(lastsky, basename); -}; - -COMMAND(loadsky, ARG_1STR); +} +COMMAND(loadsky, ARG_1CSTR) float cursordepth = 0.9f; GLint viewport[4]; GLdouble mm[16], pm[16]; vec worldpos; Index: src/rendergl.mm ================================================================== --- src/rendergl.mm +++ src/rendergl.mm @@ -144,11 +144,12 @@ void texturereset() { curtexnum = 0; -}; +} +COMMAND(texturereset, ARG_NONE) void texture(char *aframe, char *name) { int num = curtexnum++, frame = atoi(aframe); @@ -156,14 +157,12 @@ return; mapping[num][frame] = 1; char *n = mapname[num][frame]; strcpy_s(n, name); path(n); -}; - -COMMAND(texturereset, ARG_NONE); -COMMAND(texture, ARG_2STR); +} +COMMAND(texture, ARG_2STR) int lookuptexture(int tex, int &xs, int &ys) { int frame = 0; // other frames? Index: src/rendermd2.mm ================================================================== --- src/rendermd2.mm +++ src/rendermd2.mm @@ -244,26 +244,25 @@ md2 *m = loadmodel(name); mapmodelinfo mmi = { atoi(rad), atoi(h), atoi(zoff), atoi(snap), m->loadname}; m->mmi = mmi; mapmodels.add(m); -}; +} +COMMAND(mapmodel, ARG_5STR) void mapmodelreset() { mapmodels.setsize(0); -}; +} +COMMAND(mapmodelreset, ARG_NONE) mapmodelinfo * getmminfo(int i) { return i < mapmodels.length() ? &mapmodels[i]->mmi : NULL; -}; - -COMMAND(mapmodel, ARG_5STR); -COMMAND(mapmodelreset, ARG_NONE); +} void rendermodel(char *mdl, int frame, int range, int tex, float rad, float x, float y, float z, float yaw, float pitch, bool teammate, float scale, float speed, int snap, int basetime) Index: src/savegamedemo.mm ================================================================== --- src/savegamedemo.mm +++ src/savegamedemo.mm @@ -20,46 +20,51 @@ void gzput(int i) { gzputc(f, i); -}; +} + void gzputi(int i) { gzwrite(f, &i, sizeof(int)); -}; +} + void gzputv(vec &v) { gzwrite(f, &v, sizeof(vec)); -}; +} void gzcheck(int a, int b) { if (a != b) fatal("savegame file corrupt (short)"); -}; +} + int gzget() { char c = gzgetc(f); return c; -}; +} + int gzgeti() { int i; gzcheck(gzread(f, &i, sizeof(int)), sizeof(int)); return i; -}; +} + void gzgetv(vec &v) { gzcheck(gzread(f, &v, sizeof(vec)), sizeof(vec)); -}; +} void stop() { if (f) { @@ -71,18 +76,18 @@ demorecording = false; demoplayback = false; demoloading = false; loopv(playerhistory) zapdynent(playerhistory[i]); playerhistory.setsize(0); -}; +} void stopifrecording() { if (demorecording) stop(); -}; +} void savestate(char *fn) { stop(); @@ -93,11 +98,13 @@ }; gzwrite(f, (void *)"CUBESAVE", 8); gzputc(f, islittleendian); gzputi(SAVEGAMEVERSION); gzputi(sizeof(dynent)); - gzwrite(f, getclientmap(), _MAXDEFSTR); + @autoreleasepool { + gzwrite(f, getclientmap().UTF8String, _MAXDEFSTR); + } gzputi(gamemode); gzputi(ents.length()); loopv(ents) gzputc(f, ents[i].spawned); gzwrite(f, player1, sizeof(dynent)); dvector &monsters = getmonsters(); @@ -121,10 +128,11 @@ sprintf_sd(fn)("savegames/%s.csgz", name); savestate(fn); stop(); conoutf(@"wrote %s", fn); } +COMMAND(savegame, ARG_1CSTR) void loadstate(char *fn) { stop(); @@ -146,12 +154,15 @@ if (gzgeti() != SAVEGAMEVERSION || gzgeti() != sizeof(dynent)) goto out; string mapname; gzread(f, mapname, _MAXDEFSTR); nextmode = gzgeti(); - changemap(mapname); // continue below once map has been loaded and - // client & server have updated + @autoreleasepool { + changemap( + @(mapname)); // continue below once map has been loaded and + // client & server have updated + } return; out: conoutf(@"aborting: savegame/demo from a different version of cube or " @"cpu architecture"); stop(); @@ -161,10 +172,11 @@ loadgame(char *name) { sprintf_sd(fn)("savegames/%s.csgz", name); loadstate(fn); } +COMMAND(loadgame, ARG_1CSTR) void loadgameout() { stop(); @@ -245,11 +257,12 @@ gzputi(cn); conoutf(@"started recording demo to %s", fn); demorecording = true; starttime = lastmillis; ddamage = bdamage = 0; -}; +} +COMMAND(record, ARG_1CSTR) void demodamage(int damage, vec &o) { ddamage = damage; @@ -297,10 +310,11 @@ { sprintf_sd(fn)("demos/%s.cdgz", name); loadstate(fn); demoloading = true; } +COMMAND(demo, ARG_1CSTR) void stopreset() { conoutf(@"demo stopped (%d msec elapsed)", lastmillis - starttime); @@ -486,13 +500,7 @@ if (demoplayback) stopreset(); else stop(); conoutf(@"demo stopped"); -}; - -COMMAND(record, ARG_1STR); -COMMAND(demo, ARG_1STR); -COMMANDN(stop, stopn, ARG_NONE); - -COMMAND(savegame, ARG_1STR); -COMMAND(loadgame, ARG_1STR); +} +COMMANDN(stop, stopn, ARG_NONE) Index: src/serverbrowser.mm ================================================================== --- src/serverbrowser.mm +++ src/serverbrowser.mm @@ -310,15 +310,15 @@ else { servers.setsize(0); execute((char *)reply); }; servermenu(); -}; +} -COMMAND(addserver, ARG_1STR); -COMMAND(servermenu, ARG_NONE); -COMMAND(updatefrommaster, ARG_NONE); +COMMAND(addserver, ARG_1CSTR) +COMMAND(servermenu, ARG_NONE) +COMMAND(updatefrommaster, ARG_NONE) void writeservercfg() { FILE *f = fopen("servers.cfg", "w"); Index: src/sound.mm ================================================================== --- src/sound.mm +++ src/sound.mm @@ -108,13 +108,13 @@ } else { conoutf(@"could not play music: %s", sn); }; #endif }; -}; +} -COMMAND(music, ARG_1STR); +COMMAND(music, ARG_1CSTR) #ifdef USE_MIXER vector samples; #else vector samples; @@ -129,11 +129,11 @@ snames.add(newstring(name)); samples.add(NULL); return samples.length() - 1; }; -COMMAND(registersound, ARG_1EST); +COMMAND(registersound, ARG_1EST) void cleansound() { if (nosound) @@ -265,7 +265,7 @@ void sound(int n) { playsound(n, NULL); -}; -COMMAND(sound, ARG_1INT); +} +COMMAND(sound, ARG_1INT) Index: src/tools.h ================================================================== --- src/tools.h +++ src/tools.h @@ -164,26 +164,29 @@ void *alloc(size_t size); void dealloc(void *p, size_t size); void *realloc(void *p, size_t oldsize, size_t newsize); - char *string(char *s, size_t l); + char *string(const char *s, size_t l); + char * - string(char *s) + string(const char *s) { return string(s, strlen(s)); - }; + } + void deallocstr(char *s) { dealloc(s, strlen(s) + 1); - }; + } + char * - stringbuf(char *s) + stringbuf(const char *s) { return string(s, _MAXDEFSTR - 1); - }; + } void dealloc_block(void *b); void allocnext(size_t allocsize); }; @@ -377,21 +380,23 @@ t e = &ht->enumc->data; \ b; \ } inline char * -newstring(char *s) +newstring(const char *s) { return gp()->string(s); -}; +} + inline char * -newstring(char *s, size_t l) +newstring(const char *s, size_t l) { return gp()->string(s, l); -}; +} + inline char * -newstringbuf(char *s) +newstringbuf(const char *s) { return gp()->stringbuf(s); -}; +} #endif Index: src/tools.mm ================================================================== --- src/tools.mm +++ src/tools.mm @@ -80,17 +80,17 @@ p = b + PTRSIZE; left = allocsize; }; char * -pool::string(char *s, size_t l) +pool::string(const char *s, size_t l) { char *b = (char *)alloc(l + 1); strncpy(b, s, l); b[l] = 0; return b; -}; +} pool * gp() // useful for global buffers that need to be initialisation order // independant { Index: src/weapon.mm ================================================================== --- src/weapon.mm +++ src/weapon.mm @@ -62,13 +62,12 @@ void weapon(char *a1, char *a2, char *a3) { selectgun(a1[0] ? atoi(a1) : -1, a2[0] ? atoi(a2) : -1, a3[0] ? atoi(a3) : -1); -}; - -COMMAND(weapon, ARG_3STR); +} +COMMAND(weapon, ARG_3STR) void createrays(vec &from, vec &to) // create random spread of rays for the shotgun { vdist(dist, dvec, from, to); Index: src/world.mm ================================================================== --- src/world.mm +++ src/world.mm @@ -64,13 +64,12 @@ sprintf_sd(aliasname)("level_trigger_%d", tag); if (identexists(aliasname)) execute(aliasname); if (type == 2) endsp(false); -}; - -COMMAND(trigger, ARG_2INT); +} +COMMAND(trigger, ARG_2INT) // main geometric mipmapping routine, recursively rebuild mipmaps within block // b. tries to produce cube out of 4 lower level mips as well as possible, sets // defer to 0 if mipped cube is a perfect mip, i.e. can be rendered at this mip // level indistinguishable from its constituent cubes (saves considerable @@ -358,13 +357,12 @@ if (e.type == type) e.type = NOTUSED; }; if (type == LIGHT) calclight(); -}; - -COMMAND(clearents, ARG_1STR); +} +COMMAND(clearents, ARG_1CSTR) void scalecomp(uchar &c, int intens) { int n = c * intens / 100; @@ -391,13 +389,12 @@ scalecomp(e.attr3, intens); scalecomp(e.attr4, intens); }; }; calclight(); -}; - -COMMAND(scalelights, ARG_2INT); +} +COMMAND(scalelights, ARG_2INT) int findentity(int type, int index) { for (int i = index; i < ents.length(); i++) @@ -502,10 +499,10 @@ newmap(int i) { empty_world(i, false); }; -COMMAND(mapenlarge, ARG_NONE); -COMMAND(newmap, ARG_1INT); -COMMANDN(recalc, calclight, ARG_NONE); -COMMAND(delent, ARG_NONE); -COMMAND(entproperty, ARG_2INT); +COMMAND(mapenlarge, ARG_NONE) +COMMAND(newmap, ARG_1INT) +COMMANDN(recalc, calclight, ARG_NONE) +COMMAND(delent, ARG_NONE) +COMMAND(entproperty, ARG_2INT) Index: src/worldio.mm ================================================================== --- src/worldio.mm +++ src/worldio.mm @@ -9,31 +9,31 @@ rename(name, backupname); }; string cgzname, bakname, pcfname, mcfname; -void -setnames(char *name) +static void +setnames(const char *name) { string pakname, mapname; - char *slash = strpbrk(name, "/\\"); + const char *slash = strpbrk(name, "/\\"); if (slash) { strn0cpy(pakname, name, slash - name + 1); strcpy_s(mapname, slash + 1); } else { strcpy_s(pakname, "base"); strcpy_s(mapname, name); - }; + } sprintf_s(cgzname)("packages/%s/%s.cgz", pakname, mapname); sprintf_s(bakname)( "packages/%s/%s_%d.BAK", pakname, mapname, lastmillis); sprintf_s(pcfname)("packages/%s/package.cfg", pakname); sprintf_s(mcfname)("packages/%s/%s.cfg", pakname, mapname); path(cgzname); path(bakname); -}; +} // the optimize routines below are here to reduce the detrimental effects of // messy mapping by setting certain properties (vdeltas and textures) to // neighbouring values wherever there is no visible difference. This allows the // mipmapper to generate more efficient mips. the reason it is done on save is @@ -135,11 +135,11 @@ fclose(f); conoutf(@"wrote map %s as file %s", mname, cgzname); } uchar * -readmap(char *mname, int *msize) +readmap(const char *mname, int *msize) { setnames(mname); uchar *mdata = (uchar *)loadfile(cgzname, msize); if (!mdata) { conoutf(@"could not read map %s", cgzname); @@ -152,41 +152,43 @@ // run-length encoding and leaves out data for certain kinds of cubes, then zlib // removes the last bits of redundancy. Both passes contribute greatly to the // miniscule map sizes. void -save_world(char *mname) -{ - resettagareas(); // wouldn't be able to reproduce tagged areas otherwise - voptimize(); - toptimize(); - if (!*mname) - mname = getclientmap(); - setnames(mname); - backup(cgzname, bakname); - gzFile f = gzopen(cgzname, "wb9"); - if (!f) { - conoutf(@"could not write map to %s", cgzname); - return; - }; - hdr.version = MAPVERSION; - hdr.numents = 0; - loopv(ents) if (ents[i].type != NOTUSED) hdr.numents++; - header tmp = hdr; - endianswap(&tmp.version, sizeof(int), 4); - endianswap(&tmp.waterlevel, sizeof(int), 16); - gzwrite(f, &tmp, sizeof(header)); - loopv(ents) - { - if (ents[i].type != NOTUSED) { - entity tmp = ents[i]; - endianswap(&tmp, sizeof(short), 4); - gzwrite(f, &tmp, sizeof(persistent_entity)); - }; - }; - sqr *t = NULL; - int sc = 0; +save_world(const char *mname) +{ + @autoreleasepool { + resettagareas(); // wouldn't be able to reproduce tagged areas + // otherwise + voptimize(); + toptimize(); + if (!*mname) + mname = getclientmap().UTF8String; + setnames(mname); + backup(cgzname, bakname); + gzFile f = gzopen(cgzname, "wb9"); + if (!f) { + conoutf(@"could not write map to %s", cgzname); + return; + }; + hdr.version = MAPVERSION; + hdr.numents = 0; + loopv(ents) if (ents[i].type != NOTUSED) hdr.numents++; + header tmp = hdr; + endianswap(&tmp.version, sizeof(int), 4); + endianswap(&tmp.waterlevel, sizeof(int), 16); + gzwrite(f, &tmp, sizeof(header)); + loopv(ents) + { + if (ents[i].type != NOTUSED) { + entity tmp = ents[i]; + endianswap(&tmp, sizeof(short), 4); + gzwrite(f, &tmp, sizeof(persistent_entity)); + }; + }; + sqr *t = NULL; + int sc = 0; #define spurge \ while (sc) { \ gzputc(f, 255); \ if (sc > 255) { \ gzputc(f, 255); \ @@ -194,54 +196,55 @@ } else { \ gzputc(f, sc); \ sc = 0; \ } \ }; - loopk(cubicsize) - { - sqr *s = &world[k]; -#define c(f) (s->f == t->f) - // 4 types of blocks, to compress a bit: - // 255 (2): same as previous block + count - // 254 (3): same as previous, except light // deprecated - // SOLID (5) - // anything else (9) - - if (SOLID(s)) { - if (t && c(type) && c(wtex) && c(vdelta)) { - sc++; - } else { - spurge; - gzputc(f, s->type); - gzputc(f, s->wtex); - gzputc(f, s->vdelta); - }; - } else { - if (t && c(type) && c(floor) && c(ceil) && c(ctex) && - c(ftex) && c(utex) && c(wtex) && c(vdelta) && - c(tag)) { - sc++; - } else { - spurge; - gzputc(f, s->type); - gzputc(f, s->floor); - gzputc(f, s->ceil); - gzputc(f, s->wtex); - gzputc(f, s->ftex); - gzputc(f, s->ctex); - gzputc(f, s->vdelta); - gzputc(f, s->utex); - gzputc(f, s->tag); - }; - }; - t = s; - }; - spurge; - gzclose(f); - conoutf(@"wrote map file %s", cgzname); - settagareas(); -}; + loopk(cubicsize) + { + sqr *s = &world[k]; +#define c(f) (s->f == t->f) + // 4 types of blocks, to compress a bit: + // 255 (2): same as previous block + count + // 254 (3): same as previous, except light // deprecated + // SOLID (5) + // anything else (9) + + if (SOLID(s)) { + if (t && c(type) && c(wtex) && c(vdelta)) { + sc++; + } else { + spurge; + gzputc(f, s->type); + gzputc(f, s->wtex); + gzputc(f, s->vdelta); + }; + } else { + if (t && c(type) && c(floor) && c(ceil) && + c(ctex) && c(ftex) && c(utex) && c(wtex) && + c(vdelta) && c(tag)) { + sc++; + } else { + spurge; + gzputc(f, s->type); + gzputc(f, s->floor); + gzputc(f, s->ceil); + gzputc(f, s->wtex); + gzputc(f, s->ftex); + gzputc(f, s->ctex); + gzputc(f, s->vdelta); + gzputc(f, s->utex); + gzputc(f, s->tag); + }; + }; + t = s; + }; + spurge; + gzclose(f); + conoutf(@"wrote map file %s", cgzname); + settagareas(); + } +} void load_world(char *mname) // still supports all map formats that have existed // since the earliest cube betas! { @@ -369,8 +372,8 @@ alias(aliasname, ""); }; execfile("data/default_map_settings.cfg"); execfile(pcfname); execfile(mcfname); -}; +} -COMMANDN(savemap, save_world, ARG_1STR); +COMMANDN(savemap, save_world, ARG_1CSTR) Index: src/worldocull.mm ================================================================== --- src/worldocull.mm +++ src/worldocull.mm @@ -10,13 +10,12 @@ void toggleocull() { ocull = !ocull; -}; - -COMMAND(toggleocull, ARG_NONE); +} +COMMAND(toggleocull, ARG_NONE) // constructs occlusion map: cast rays in all directions on the 2d plane and // record distance. done exactly once per frame. void