Index: src/MD2.mm ================================================================== --- src/MD2.mm +++ src/MD2.mm @@ -61,11 +61,11 @@ handlerForIRI:IRI] openItemAtIRI:IRI mode:@"r"]; } @catch (id e) { return false; } - if (![stream isKindOfClass:[OFSeekableStream class]]) + if (![stream isKindOfClass:OFSeekableStream.class]) return false; md2_header header; [stream readIntoBuffer:&header exactLength:sizeof(md2_header)]; endianswap( Index: src/MenuItem.m ================================================================== --- src/MenuItem.m +++ src/MenuItem.m @@ -13,11 +13,11 @@ - (OFComparisonResult)compare:(id)otherObject { MenuItem *otherItem; - if (![otherObject isKindOfClass:[MenuItem class]]) + if (![otherObject isKindOfClass:MenuItem.class]) @throw [OFInvalidArgumentException exception]; int x = (int)_text.longLongValue; int y = (int)otherItem.text.longLongValue; Index: src/commands.mm ================================================================== --- src/commands.mm +++ src/commands.mm @@ -26,11 +26,11 @@ if (identifiers == nil) identifiers = [[OFMutableDictionary alloc] init]; identifiers[name] = alias; } else { - if ([alias isKindOfClass:[Alias class]]) + if ([alias isKindOfClass:Alias.class]) alias.action = action; else conoutf( @"cannot redefine builtin %@ with an alias", name); } @@ -77,11 +77,11 @@ OFString * getalias(OFString *name) { Alias *alias = identifiers[name]; - if ([alias isKindOfClass:[Alias class]]) + if ([alias isKindOfClass:Alias.class]) return alias.action; return nil; } @@ -168,14 +168,14 @@ { @autoreleasepool { __kindof Identifier *identifier = identifiers[[n substringFromIndex:1]]; - if ([identifier isKindOfClass:[Variable class]]) { + if ([identifier isKindOfClass:Variable.class]) { return [OFString stringWithFormat:@"%d", *[identifier storage]]; - } else if ([identifier isKindOfClass:[Alias class]]) + } else if ([identifier isKindOfClass:Alias.class]) return [identifier action]; } conoutf(@"unknown alias lookup: %@", [n substringFromIndex:1]); return n; @@ -236,12 +236,11 @@ val = (int)[c longLongValueWithBase:0]; } @catch (OFInvalidFormatException *e) { conoutf(@"unknown command: %@", c); } } else { - if ([identifier - isKindOfClass:[Command class]]) { + if ([identifier isKindOfClass:Command.class]) { // game defined commands use very // ad-hoc function signature, and just // call it OFArray *arguments = [[OFArray alloc] @@ -249,12 +248,11 @@ count:numargs]; val = [identifier callWithArguments:arguments isDown:isDown]; } else if ([identifier - isKindOfClass:[Variable - class]]) { + isKindOfClass:Variable.class]) { // game defined variables if (isDown) { if (w[1].length == 0) [identifier printValue]; else @@ -263,11 +261,11 @@ (int)[w[1] longLongValueWithBase: 0]]; } } else if ([identifier - isKindOfClass:[Alias class]]) { + isKindOfClass:Alias.class]) { // alias, also used as functions and // (global) variables for (int i = 1; i < numargs; i++) { // set any arguments as // (global) arg values so @@ -390,11 +388,11 @@ writeclientinfo(stream); [stream writeString:@"\n"]; [identifiers enumerateKeysAndObjectsUsingBlock:^( OFString *name, __kindof Identifier *identifier, bool *stop) { - if (![identifier isKindOfClass:[Variable class]] || + if (![identifier isKindOfClass:Variable.class] || ![identifier persisted]) return; [stream writeFormat:@"%@ %d\n", identifier.name, *[identifier storage]]; @@ -404,11 +402,11 @@ writebinds(stream); [stream writeString:@"\n"]; [identifiers enumerateKeysAndObjectsUsingBlock:^( OFString *name, __kindof Identifier *identifier, bool *stop) { - if (![identifier isKindOfClass:[Alias class]] || + if (![identifier isKindOfClass:Alias.class] || [identifier.name hasPrefix:@"nextmap_"]) return; [stream writeFormat:@"alias \"%@\" [%@]\n", identifier.name, [identifier action]]; Index: src/serverbrowser.mm ================================================================== --- src/serverbrowser.mm +++ src/serverbrowser.mm @@ -2,213 +2,269 @@ // management #include "SDL_thread.h" #include "cube.h" -struct resolverthread { - SDL_Thread *thread; - char *query; - int starttime; -}; - -struct resolverresult { - char *query; - ENetAddress address; -}; - -vector resolverthreads; -vector resolverqueries; -vector resolverresults; -SDL_mutex *resolvermutex; -SDL_sem *resolversem; -int resolverlimit = 1000; - -int -resolverloop(void *data) -{ - resolverthread *rt = (resolverthread *)data; - for (;;) { +@interface ResolverThread: OFThread +{ + volatile bool _stop; +} + +@property (copy, nonatomic) OFString *query; +@property (nonatomic) int starttime; +@end + +@interface ResolverResult: OFObject +@property (readonly, nonatomic) OFString *query; +@property (readonly, nonatomic) ENetAddress address; + +- (instancetype)init OF_UNAVAILABLE; +- (instancetype)initWithQuery:(OFString *)query address:(ENetAddress)address; +@end + +static OFMutableArray *resolverthreads; +static OFMutableArray *resolverqueries; +static OFMutableArray *resolverresults; +static SDL_sem *resolversem; +static int resolverlimit = 1000; + +@implementation ResolverThread +- (id)main +{ + while (!_stop) { SDL_SemWait(resolversem); - SDL_LockMutex(resolvermutex); - if (resolverqueries.empty()) { - SDL_UnlockMutex(resolvermutex); - continue; - } - rt->query = resolverqueries.pop(); - rt->starttime = lastmillis; - SDL_UnlockMutex(resolvermutex); + + @synchronized(ResolverThread.class) { + if (resolverqueries.count == 0) + continue; + + _query = resolverqueries.lastObject; + [resolverqueries + removeObjectAtIndex:resolverqueries.count - 1]; + _starttime = lastmillis; + } + ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; - enet_address_set_host(&address, rt->query); - SDL_LockMutex(resolvermutex); - resolverresult &rr = resolverresults.add(); - rr.query = rt->query; - rr.address = address; - rt->query = NULL; - rt->starttime = 0; - SDL_UnlockMutex(resolvermutex); - } - return 0; -} + enet_address_set_host(&address, _query.UTF8String); + + @synchronized(ResolverThread.class) { + [resolverresults addObject:[[ResolverResult alloc] + initWithQuery:_query + address:address]]; + + _query = NULL; + _starttime = 0; + } + } + + return nil; +} + +- (void)stop +{ + _stop = true; +} +@end + +@implementation ResolverResult +- (instancetype)initWithQuery:(OFString *)query address:(ENetAddress)address +{ + self = [super init]; + + _query = query; + _address = address; + + return self; +} +@end void resolverinit(int threads, int limit) { + resolverthreads = [[OFMutableArray alloc] init]; + resolverqueries = [[OFMutableArray alloc] init]; + resolverresults = [[OFMutableArray alloc] init]; resolverlimit = limit; resolversem = SDL_CreateSemaphore(0); - resolvermutex = SDL_CreateMutex(); while (threads > 0) { - resolverthread &rt = resolverthreads.add(); - rt.query = NULL; - rt.starttime = 0; - rt.thread = - SDL_CreateThread(resolverloop, "resolverthread", &rt); + ResolverThread *rt = [[ResolverThread alloc] init]; + rt.name = @"resolverthread"; + [resolverthreads addObject:rt]; + [rt start]; --threads; } } void -resolverstop(resolverthread &rt, bool restart) -{ - SDL_LockMutex(resolvermutex); - // SDL_KillThread(rt.thread); - rt.query = NULL; - rt.starttime = 0; - rt.thread = NULL; - if (restart) - rt.thread = - SDL_CreateThread(resolverloop, "resolverthread", &rt); - SDL_UnlockMutex(resolvermutex); +resolverstop(size_t i, bool restart) +{ + @synchronized(ResolverThread.class) { + ResolverThread *rt = resolverthreads[i]; + [rt stop]; + + if (restart) { + rt = [[ResolverThread alloc] init]; + rt.name = @"resolverthread"; + + resolverthreads[i] = rt; + + [rt start]; + } else + [resolverthreads removeObjectAtIndex:i]; + } } void resolverclear() { - SDL_LockMutex(resolvermutex); - resolverqueries.setsize(0); - resolverresults.setsize(0); - while (SDL_SemTryWait(resolversem) == 0) - ; - loopv(resolverthreads) - { - resolverthread &rt = resolverthreads[i]; - resolverstop(rt, true); - } - SDL_UnlockMutex(resolvermutex); + @synchronized(ResolverThread.class) { + [resolverqueries removeAllObjects]; + [resolverresults removeAllObjects]; + + while (SDL_SemTryWait(resolversem) == 0) + ; + + for (size_t i = 0; i < resolverthreads.count; i++) + resolverstop(i, true); + } } void -resolverquery(char *name) +resolverquery(OFString *name) { - SDL_LockMutex(resolvermutex); - resolverqueries.add(name); - SDL_SemPost(resolversem); - SDL_UnlockMutex(resolvermutex); + @synchronized(ResolverThread.class) { + [resolverqueries addObject:name]; + SDL_SemPost(resolversem); + } } bool -resolvercheck(char **name, ENetAddress *address) -{ - SDL_LockMutex(resolvermutex); - if (!resolverresults.empty()) { - resolverresult &rr = resolverresults.pop(); - *name = rr.query; - *address = rr.address; - SDL_UnlockMutex(resolvermutex); - return true; - } - loopv(resolverthreads) - { - resolverthread &rt = resolverthreads[i]; - if (rt.query) { - if (lastmillis - rt.starttime > resolverlimit) { - resolverstop(rt, true); - *name = rt.query; - SDL_UnlockMutex(resolvermutex); - return true; - } - } - } - SDL_UnlockMutex(resolvermutex); - return false; -} - -struct serverinfo { - string name; - string full; - string map; - string sdesc; - int mode, numplayers, ping, protocol, minremain; - ENetAddress address; -}; - -vector servers; -ENetSocket pingsock = ENET_SOCKET_NULL; -int lastinfo = 0; +resolvercheck(OFString **name, ENetAddress *address) +{ + @synchronized(ResolverThread.class) { + if (resolverresults.count > 0) { + ResolverResult *rr = resolverresults.lastObject; + *name = rr.query; + *address = rr.address; + [resolverresults + removeObjectAtIndex:resolverresults.count - 1]; + return true; + } + + for (size_t i = 0; i < resolverthreads.count; i++) { + ResolverThread *rt = resolverthreads[i]; + + if (rt.query) { + if (lastmillis - rt.starttime > resolverlimit) { + resolverstop(i, true); + *name = rt.query; + return true; + } + } + } + } + + return false; +} + +@interface ServerInfo: OFObject +@property (nonatomic) OFString *name; +@property (nonatomic) OFString *full; +@property (nonatomic) OFString *map; +@property (nonatomic) OFString *sdesc; +@property (nonatomic) int mode, numplayers, ping, protocol, minremain; +@property (nonatomic) ENetAddress address; +@end + +@implementation ServerInfo +- (OFComparisonResult)compare:(id)otherObject +{ + if (![otherObject isKindOfClass:ServerInfo.class]) + @throw [OFInvalidArgumentException exception]; + + if (_ping > [otherObject ping]) + return OFOrderedDescending; + if (_ping < [otherObject ping]) + return OFOrderedAscending; + + return [_name compare:[otherObject name]]; +} +@end + +static OFMutableArray *servers; +static ENetSocket pingsock = ENET_SOCKET_NULL; +static int lastinfo = 0; OFString * getservername(int n) { - @autoreleasepool { - return @(servers[n].name); - } -} - -void -addserver(OFString *servername_) -{ - @autoreleasepool { - const char *servername = servername_.UTF8String; - loopv(servers) if (strcmp(servers[i].name, servername) == - 0) return; - serverinfo &si = servers.insert(0, serverinfo()); - strcpy_s(si.name, servername); - si.full[0] = 0; + return servers[n].name; +} + +void +addserver(OFString *servername) +{ + @autoreleasepool { + for (ServerInfo *si in servers) + if ([si.name isEqual:servername]) + return; + + ServerInfo *si = [[ServerInfo alloc] init]; + si.name = servername; + si.full = @""; si.mode = 0; si.numplayers = 0; si.ping = 9999; si.protocol = 0; si.minremain = 0; - si.map[0] = 0; - si.sdesc[0] = 0; - si.address.host = ENET_HOST_ANY; - si.address.port = CUBE_SERVINFO_PORT; + si.map = @""; + si.sdesc = @""; + ENetAddress address = { .host = ENET_HOST_ANY, + .port = CUBE_SERVINFO_PORT }; + si.address = address; + + if (servers == nil) + servers = [[OFMutableArray alloc] init]; + + [servers addObject:si]; } } void pingservers() { ENetBuffer buf; uchar ping[MAXTRANS]; uchar *p; - loopv(servers) - { - serverinfo &si = servers[i]; + + for (ServerInfo *si in servers) { if (si.address.host == ENET_HOST_ANY) continue; + p = ping; putint(p, lastmillis); buf.data = ping; buf.dataLength = p - ping; - enet_socket_send(pingsock, &si.address, &buf, 1); + ENetAddress address = si.address; + enet_socket_send(pingsock, &address, &buf, 1); } + lastinfo = lastmillis; } void checkresolver() { - char *name = NULL; + OFString *name = nil; ENetAddress addr = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; while (resolvercheck(&name, &addr)) { if (addr.host == ENET_HOST_ANY) continue; - loopv(servers) - { - serverinfo &si = servers[i]; - if (name == si.name) { + + for (ServerInfo *si in servers) { + if ([name isEqual:si.name]) { si.address = addr; addr.host = ENET_HOST_ANY; break; } } @@ -223,16 +279,16 @@ ENetAddress addr; uchar ping[MAXTRANS], *p; char text[MAXTRANS]; buf.data = ping; buf.dataLength = sizeof(ping); + while (enet_socket_wait(pingsock, &events, 0) >= 0 && events) { if (enet_socket_receive(pingsock, &addr, &buf, 1) <= 0) return; - loopv(servers) - { - serverinfo &si = servers[i]; + + for (ServerInfo *si in servers) { if (addr.host == si.address.host) { p = ping; si.ping = lastmillis - getint(p); si.protocol = getint(p); if (si.protocol != PROTOCOL_VERSION) @@ -239,65 +295,67 @@ si.ping = 9998; si.mode = getint(p); si.numplayers = getint(p); si.minremain = getint(p); sgetstr(); - strcpy_s(si.map, text); + @autoreleasepool { + si.map = @(text); + } sgetstr(); - strcpy_s(si.sdesc, text); + @autoreleasepool { + si.sdesc = @(text); + } break; } } } } -int -sicompare(const serverinfo *a, const serverinfo *b) -{ - return a->ping > b->ping - ? 1 - : (a->ping < b->ping ? -1 : strcmp(a->name, b->name)); -} - void refreshservers() { checkresolver(); checkpings(); if (lastmillis - lastinfo >= 5000) pingservers(); - servers.sort((void *)sicompare); + [servers sort]; int maxmenu = 16; - loopv(servers) - { - serverinfo &si = servers[i]; + + size_t i = 0; + for (ServerInfo *si in servers) { if (si.address.host != ENET_HOST_ANY && si.ping != 9999) { if (si.protocol != PROTOCOL_VERSION) - sprintf_s(si.full)( - "%s [different cube protocol]", si.name); - else { - @autoreleasepool { - sprintf_s(si.full)( - "%d\t%d\t%s, %s: %s %s", si.ping, - si.numplayers, - si.map[0] ? si.map : "[unknown]", - modestr(si.mode).UTF8String, - si.name, si.sdesc); - } - } - } else { - sprintf_s(si.full)(si.address.host != ENET_HOST_ANY - ? "%s [waiting for server response]" - : "%s [unknown host]\t", - si.name); - } - si.full[50] = 0; // cut off too long server descriptions - @autoreleasepool { - menumanual(1, i, @(si.full)); - } + si.full = [[OFString alloc] + initWithFormat: + @"%@ [different cube protocol]", + si.name]; + else + si.full = [[OFString alloc] + initWithFormat:@"%d\t%d\t%@, %@: %@ %@", + si.ping, si.numplayers, + si.map.length > 0 ? si.map : @"[unknown]", + modestr(si.mode), si.name, si.sdesc]; + } else + si.full = [[OFString alloc] + initWithFormat: + (si.address.host != ENET_HOST_ANY + ? @"%@ [waiting for server response]" + : @"%@ [unknown host]\t"), + si.name]; + + // cut off too long server descriptions + @autoreleasepool { + if (si.full.length > 50) + si.full = [si.full substringToIndex:50]; + } + + menumanual(1, i, si.full); + if (!--maxmenu) return; + + i++; } } void servermenu() @@ -304,12 +362,16 @@ { if (pingsock == ENET_SOCKET_NULL) { pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL); resolverinit(1, 1000); } + resolverclear(); - loopv(servers) resolverquery(servers[i].name); + + for (ServerInfo *si in servers) + resolverquery(si.name); + refreshservers(); menuset(1); } void @@ -320,11 +382,11 @@ uchar *reply = retrieveservers(buf, MAXUPD); if (!*reply || strstr((char *)reply, "") || strstr((char *)reply, "")) conoutf(@"master server not replying"); else { - servers.setsize(0); + [servers removeAllObjects]; @autoreleasepool { execute(@((char *)reply)); } } servermenu(); @@ -339,8 +401,11 @@ { FILE *f = fopen("servers.cfg", "w"); if (!f) return; fprintf(f, "// servers connected to are added here automatically\n\n"); - loopvrev(servers) fprintf(f, "addserver %s\n", servers[i].name); + @autoreleasepool { + for (ServerInfo *si in servers.reversedArray) + fprintf(f, "addserver %s\n", si.name.UTF8String); + } fclose(f); }