Index: src/server.mm ================================================================== --- src/server.mm +++ src/server.mm @@ -3,21 +3,24 @@ #include "cube.h" enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; -struct client // server side version of "dynent" type -{ - int type; - ENetPeer *peer; - string hostname; - string mapvote; - string name; - int modevote; -}; - -vector clients; +// server side version of "dynent" type +@interface Client: OFObject +@property (nonatomic) int type; +@property (nonatomic) ENetPeer *peer; +@property (copy, nonatomic) OFString *hostname; +@property (copy, nonatomic) OFString *mapvote; +@property (copy, nonatomic) OFString *name; +@property (nonatomic) int modevote; +@end + +@implementation Client +@end + +static OFMutableArray *clients; int maxclients = 8; static OFString *smapname; // server side version of "entity" type @@ -26,12 +29,12 @@ int spawnsecs; }; vector sents; -bool notgotitems = - true; // true when map has changed and waiting for clients to send item +// true when map has changed and waiting for clients to send item +bool notgotitems = true; int mode = 0; void restoreserverstate( vector &ents) // hack: called from savegame code, only works in SP @@ -54,24 +57,23 @@ #define MAXOBUF 100000 void process(ENetPacket *packet, int sender); void multicast(ENetPacket *packet, int sender); -void disconnect_client(int n, char *reason); +void disconnect_client(int n, OFString *reason); void send(int n, ENetPacket *packet) { if (!packet) return; + switch (clients[n].type) { - case ST_TCPIP: { + case ST_TCPIP: enet_peer_send(clients[n].peer, 0, packet); bsend += packet->dataLength; break; - } - case ST_LOCAL: localservertoclient(packet->data, packet->dataLength); break; } } @@ -110,13 +112,14 @@ if (packet->referenceCount == 0) enet_packet_destroy(packet); } void -disconnect_client(int n, char *reason) +disconnect_client(int n, OFString *reason) { - printf("disconnecting client (%s) [%s]\n", clients[n].hostname, reason); + [OFStdOut writeFormat:@"disconnecting client (%@) [%@]\n", + clients[n].hostname, reason]; enet_peer_disconnect(clients[n].peer); clients[n].type = ST_EMPTY; send2(true, -1, SV_CDIS, n); } @@ -141,41 +144,48 @@ } void resetvotes() { - loopv(clients) clients[i].mapvote[0] = 0; + for (Client *client in clients) + client.mapvote = @""; } bool -vote(char *map, int reqmode, int sender) +vote(OFString *map, int reqmode, int sender) { - strcpy_s(clients[sender].mapvote, map); + 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++; - } + + int yes = 0, no = 0; + for (Client *client in clients) { + if (client.type != ST_EMPTY) { + if (client.mapvote.length > 0) { + if ([client.mapvote isEqual:map] && + client.modevote == reqmode) + yes++; + else + no++; + } else + no++; + } + } + if (yes == 1 && no == 0) return true; // single player + @autoreleasepool { OFString *msg = [OFString stringWithFormat: - @"%s suggests %@ on map %s (set map to vote)", + @"%@ suggests %@ on map %@ (set map to vote)", clients[sender].name, modestr(reqmode), map]; sendservmsg(msg); } + if (yes / (float)(yes + no) <= 0.5f) return false; + sendservmsg(@"vote passed"); resetvotes(); return true; } @@ -186,11 +196,11 @@ void process(ENetPacket *packet, int sender) // sender may be -1 { if (ENET_NET_TO_HOST_16(*(ushort *)packet->data) != packet->dataLength) { - disconnect_client(sender, "packet length"); + disconnect_client(sender, @"packet length"); return; } uchar *end = packet->data + packet->dataLength; uchar *p = packet->data + 2; @@ -203,23 +213,27 @@ sgetstr(); break; case SV_INITC2S: sgetstr(); - strcpy_s(clients[cn].name, text); + @autoreleasepool { + clients[cn].name = @(text); + } sgetstr(); getint(p); break; case SV_MAPCHANGE: { sgetstr(); int reqmode = getint(p); if (reqmode < 0) reqmode = 0; - if (smapname.length > 0 && !mapreload && - !vote(text, reqmode, sender)) - return; + @autoreleasepool { + if (smapname.length > 0 && !mapreload && + !vote(@(text), reqmode, sender)) + return; + } mapreload = false; mode = reqmode; minremain = mode & 1 ? 15 : 10; mapend = lastsec + minremain * 60; interm = 0; @@ -254,13 +268,13 @@ send2(false, cn, SV_PONG, getint(p)); break; case SV_POS: { cn = getint(p); - if (cn < 0 || cn >= clients.length() || + if (cn < 0 || cn >= clients.count || clients[cn].type == ST_EMPTY) { - disconnect_client(sender, "client num"); + disconnect_client(sender, @"client num"); return; } int size = msgsizelookup(type); assert(size != -1); loopi(size - 2) getint(p); @@ -289,19 +303,19 @@ } default: { int size = msgsizelookup(type); if (size == -1) { - disconnect_client(sender, "tag type"); + disconnect_client(sender, @"tag type"); return; } loopi(size - 1) getint(p); } } if (p > end) { - disconnect_client(sender, "end of packet"); + disconnect_client(sender, @"end of packet"); return; } multicast(packet, sender); } @@ -317,11 +331,11 @@ putint(p, PROTOCOL_VERSION); @autoreleasepool { putint(p, *smapname.UTF8String); } sendstring(serverpassword, p); - putint(p, clients.length() > maxclients); + putint(p, clients.count > maxclients); if (smapname.length > 0) { putint(p, SV_MAPCHANGE); sendstring(smapname, p); putint(p, mode); putint(p, SV_ITEMLIST); @@ -334,16 +348,13 @@ } void multicast(ENetPacket *packet, int sender) { - loopv(clients) - { - if (i == sender) - continue; + size_t count = clients.count; + for (size_t i = 0; i < count; i++) send(i, packet); - } } void localclienttoserver(ENetPacket *packet) { @@ -350,15 +361,25 @@ process(packet, 0); if (!packet->referenceCount) enet_packet_destroy(packet); } -client & +Client * addclient() { - loopv(clients) if (clients[i].type == ST_EMPTY) return clients[i]; - return clients.add(); + for (Client *client in clients) + if (client.type == ST_EMPTY) + return client; + + Client *client = [[Client alloc] init]; + + if (clients == nil) + clients = [[OFMutableArray alloc] init]; + + [clients addObject:client]; + + return client; } void checkintermission() { @@ -377,12 +398,15 @@ } void resetserverifempty() { - loopv(clients) if (clients[i].type != ST_EMPTY) return; - clients.setsize(0); + for (Client *client in clients) + if (client.type != ST_EMPTY) + return; + + [clients removeAllObjects]; smapname = @""; resetvotes(); resetitems(); mode = 0; mapreload = false; @@ -414,35 +438,43 @@ if ((mode > 1 || (mode == 0 && nonlocalclients)) && seconds > mapend - minremain * 60) checkintermission(); if (interm && seconds > interm) { interm = 0; - loopv(clients) if (clients[i].type != ST_EMPTY) - { - send2(true, i, SV_MAPRELOAD, - 0); // ask a client to trigger map reload - mapreload = true; - break; + size_t i = 0; + for (Client *client in clients) { + if (client.type != ST_EMPTY) { + // ask a client to trigger map reload + send2(true, i, SV_MAPRELOAD, 0); + mapreload = true; + break; + } + + i++; } } resetserverifempty(); if (!isdedicated) return; // below is network only int numplayers = 0; - loopv(clients) if (clients[i].type != ST_EMPTY)++ numplayers; + for (Client *client in clients) + if (client.type != ST_EMPTY) + numplayers++; + serverms(mode, numplayers, minremain, smapname, seconds, - clients.length() >= maxclients); + clients.count >= maxclients); - if (seconds - laststatus > - 60) // display bandwidth stats, useful for server ops - { + // display bandwidth stats, useful for server ops + if (seconds - laststatus > 60) { nonlocalclients = 0; - loopv(clients) if (clients[i].type == ST_TCPIP) - nonlocalclients++; + for (Client *client in clients) + if (client.type == ST_TCPIP) + nonlocalclients++; + laststatus = seconds; if (nonlocalclients || bsend || brec) printf("status: %d remote clients, %.1f send, %.1f rec " "(K/sec)\n", nonlocalclients, bsend / 60.0f / 1024, @@ -452,22 +484,25 @@ ENetEvent event; if (enet_host_service(serverhost, &event, timeout) > 0) { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: { - client &c = addclient(); + Client *c = addclient(); c.type = ST_TCPIP; c.peer = event.peer; - c.peer->data = (void *)(&c - &clients[0]); + c.peer->data = (void *)(clients.count - 1); char hn[1024]; - strcpy_s(c.hostname, - (enet_address_get_host( - &c.peer->address, hn, sizeof(hn)) == 0) - ? hn - : "localhost"); - printf("client connected (%s)\n", c.hostname); - send_welcome(lastconnect = &c - &clients[0]); + @autoreleasepool { + c.hostname = + (enet_address_get_host( + &c.peer->address, hn, sizeof(hn)) == 0 + ? @(hn) + : @"localhost"); + } + [OFStdOut + writeFormat:@"client connected (%@)\n", c.hostname]; + send_welcome(lastconnect = clients.count - 1); break; } case ENET_EVENT_TYPE_RECEIVE: brec += event.packet->dataLength; process(event.packet, (intptr_t)event.peer->data); @@ -476,20 +511,20 @@ break; case ENET_EVENT_TYPE_DISCONNECT: if ((intptr_t)event.peer->data < 0) break; - printf("disconnected client (%s)\n", - clients[(intptr_t)event.peer->data].hostname); - clients[(intptr_t)event.peer->data].type = ST_EMPTY; + [OFStdOut writeFormat:@"disconnected client (%@)\n", + clients[(size_t)event.peer->data].hostname]; + clients[(size_t)event.peer->data].type = ST_EMPTY; send2(true, -1, SV_CDIS, (intptr_t)event.peer->data); event.peer->data = (void *)-1; break; } if (numplayers > maxclients) - disconnect_client(lastconnect, "maxclients reached"); + disconnect_client(lastconnect, @"maxclients reached"); } #ifndef _WIN32 fflush(stdout); #endif } @@ -502,21 +537,22 @@ } void localdisconnect() { - loopv(clients) if (clients[i].type == ST_LOCAL) clients[i].type = - ST_EMPTY; + for (Client *client in clients) + if (client.type == ST_LOCAL) + client.type = ST_EMPTY; } void localconnect() { - client &c = addclient(); + Client *c = addclient(); c.type = ST_LOCAL; - strcpy_s(c.hostname, "local"); - send_welcome(&c - &clients[0]); + c.hostname = @"local"; + send_welcome(clients.count - 1); } void initserver(bool dedicated, int uprate, OFString *sdesc, OFString *ip, OFString *master, OFString *passwd, int maxcl) @@ -524,11 +560,11 @@ serverpassword = passwd; maxclients = maxcl; servermsinit(master ? master : @"wouter.fov120.com/cube/masterserver/", sdesc, dedicated); - if (isdedicated = dedicated) { + if ((isdedicated = dedicated)) { ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; @autoreleasepool { if (ip.length > 0 && enet_address_set_host(&address, ip.UTF8String) < 0) printf("WARNING: server ip not resolved");