Clean up serverbrowser.mm

FossilOrigin-Name: 4d3e20926014cf9b4cbc48b2636feb3521c5514814d98799b63dec28aee39c42
This commit is contained in:
Jonathan Schleifer 2025-03-09 21:33:54 +00:00
parent 90fc249052
commit 2fe9b5079f
4 changed files with 242 additions and 179 deletions

View file

@ -63,7 +63,7 @@ snap(int sn, float f)
return false; return false;
} }
if (![stream isKindOfClass:[OFSeekableStream class]]) if (![stream isKindOfClass:OFSeekableStream.class])
return false; return false;
md2_header header; md2_header header;

View file

@ -15,7 +15,7 @@
{ {
MenuItem *otherItem; MenuItem *otherItem;
if (![otherObject isKindOfClass:[MenuItem class]]) if (![otherObject isKindOfClass:MenuItem.class])
@throw [OFInvalidArgumentException exception]; @throw [OFInvalidArgumentException exception];
int x = (int)_text.longLongValue; int x = (int)_text.longLongValue;

View file

@ -28,7 +28,7 @@ alias(OFString *name, OFString *action)
identifiers[name] = alias; identifiers[name] = alias;
} else { } else {
if ([alias isKindOfClass:[Alias class]]) if ([alias isKindOfClass:Alias.class])
alias.action = action; alias.action = action;
else else
conoutf( conoutf(
@ -79,7 +79,7 @@ getalias(OFString *name)
{ {
Alias *alias = identifiers[name]; Alias *alias = identifiers[name];
if ([alias isKindOfClass:[Alias class]]) if ([alias isKindOfClass:Alias.class])
return alias.action; return alias.action;
return nil; return nil;
@ -170,10 +170,10 @@ lookup(OFString *n) // find value of ident referenced with $ in exp
__kindof Identifier *identifier = __kindof Identifier *identifier =
identifiers[[n substringFromIndex:1]]; identifiers[[n substringFromIndex:1]];
if ([identifier isKindOfClass:[Variable class]]) { if ([identifier isKindOfClass:Variable.class]) {
return [OFString return [OFString
stringWithFormat:@"%d", *[identifier storage]]; stringWithFormat:@"%d", *[identifier storage]];
} else if ([identifier isKindOfClass:[Alias class]]) } else if ([identifier isKindOfClass:Alias.class])
return [identifier action]; return [identifier action];
} }
@ -238,8 +238,7 @@ execute(OFString *string, bool isDown)
conoutf(@"unknown command: %@", c); conoutf(@"unknown command: %@", c);
} }
} else { } else {
if ([identifier if ([identifier isKindOfClass:Command.class]) {
isKindOfClass:[Command class]]) {
// game defined commands use very // game defined commands use very
// ad-hoc function signature, and just // ad-hoc function signature, and just
// call it // call it
@ -251,8 +250,7 @@ execute(OFString *string, bool isDown)
callWithArguments:arguments callWithArguments:arguments
isDown:isDown]; isDown:isDown];
} else if ([identifier } else if ([identifier
isKindOfClass:[Variable isKindOfClass:Variable.class]) {
class]]) {
// game defined variables // game defined variables
if (isDown) { if (isDown) {
if (w[1].length == 0) if (w[1].length == 0)
@ -265,7 +263,7 @@ execute(OFString *string, bool isDown)
0]]; 0]];
} }
} else if ([identifier } else if ([identifier
isKindOfClass:[Alias class]]) { isKindOfClass:Alias.class]) {
// alias, also used as functions and // alias, also used as functions and
// (global) variables // (global) variables
for (int i = 1; i < numargs; i++) { for (int i = 1; i < numargs; i++) {
@ -392,7 +390,7 @@ writecfg()
[identifiers enumerateKeysAndObjectsUsingBlock:^( [identifiers enumerateKeysAndObjectsUsingBlock:^(
OFString *name, __kindof Identifier *identifier, bool *stop) { OFString *name, __kindof Identifier *identifier, bool *stop) {
if (![identifier isKindOfClass:[Variable class]] || if (![identifier isKindOfClass:Variable.class] ||
![identifier persisted]) ![identifier persisted])
return; return;
@ -406,7 +404,7 @@ writecfg()
[identifiers enumerateKeysAndObjectsUsingBlock:^( [identifiers enumerateKeysAndObjectsUsingBlock:^(
OFString *name, __kindof Identifier *identifier, bool *stop) { OFString *name, __kindof Identifier *identifier, bool *stop) {
if (![identifier isKindOfClass:[Alias class]] || if (![identifier isKindOfClass:Alias.class] ||
[identifier.name hasPrefix:@"nextmap_"]) [identifier.name hasPrefix:@"nextmap_"])
return; return;

View file

@ -4,174 +4,229 @@
#include "SDL_thread.h" #include "SDL_thread.h"
#include "cube.h" #include "cube.h"
struct resolverthread { @interface ResolverThread: OFThread
SDL_Thread *thread;
char *query;
int starttime;
};
struct resolverresult {
char *query;
ENetAddress address;
};
vector<resolverthread> resolverthreads;
vector<char *> resolverqueries;
vector<resolverresult> resolverresults;
SDL_mutex *resolvermutex;
SDL_sem *resolversem;
int resolverlimit = 1000;
int
resolverloop(void *data)
{ {
resolverthread *rt = (resolverthread *)data; volatile bool _stop;
for (;;) {
SDL_SemWait(resolversem);
SDL_LockMutex(resolvermutex);
if (resolverqueries.empty()) {
SDL_UnlockMutex(resolvermutex);
continue;
}
rt->query = resolverqueries.pop();
rt->starttime = lastmillis;
SDL_UnlockMutex(resolvermutex);
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;
} }
@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<ResolverThread *> *resolverthreads;
static OFMutableArray<OFString *> *resolverqueries;
static OFMutableArray<ResolverResult *> *resolverresults;
static SDL_sem *resolversem;
static int resolverlimit = 1000;
@implementation ResolverThread
- (id)main
{
while (!_stop) {
SDL_SemWait(resolversem);
@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, _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 void
resolverinit(int threads, int limit) resolverinit(int threads, int limit)
{ {
resolverthreads = [[OFMutableArray alloc] init];
resolverqueries = [[OFMutableArray alloc] init];
resolverresults = [[OFMutableArray alloc] init];
resolverlimit = limit; resolverlimit = limit;
resolversem = SDL_CreateSemaphore(0); resolversem = SDL_CreateSemaphore(0);
resolvermutex = SDL_CreateMutex();
while (threads > 0) { while (threads > 0) {
resolverthread &rt = resolverthreads.add(); ResolverThread *rt = [[ResolverThread alloc] init];
rt.query = NULL; rt.name = @"resolverthread";
rt.starttime = 0; [resolverthreads addObject:rt];
rt.thread = [rt start];
SDL_CreateThread(resolverloop, "resolverthread", &rt);
--threads; --threads;
} }
} }
void void
resolverstop(resolverthread &rt, bool restart) resolverstop(size_t i, bool restart)
{ {
SDL_LockMutex(resolvermutex); @synchronized(ResolverThread.class) {
// SDL_KillThread(rt.thread); ResolverThread *rt = resolverthreads[i];
rt.query = NULL; [rt stop];
rt.starttime = 0;
rt.thread = NULL; if (restart) {
if (restart) rt = [[ResolverThread alloc] init];
rt.thread = rt.name = @"resolverthread";
SDL_CreateThread(resolverloop, "resolverthread", &rt);
SDL_UnlockMutex(resolvermutex); resolverthreads[i] = rt;
[rt start];
} else
[resolverthreads removeObjectAtIndex:i];
}
} }
void void
resolverclear() resolverclear()
{ {
SDL_LockMutex(resolvermutex); @synchronized(ResolverThread.class) {
resolverqueries.setsize(0); [resolverqueries removeAllObjects];
resolverresults.setsize(0); [resolverresults removeAllObjects];
while (SDL_SemTryWait(resolversem) == 0)
; while (SDL_SemTryWait(resolversem) == 0)
loopv(resolverthreads) ;
{
resolverthread &rt = resolverthreads[i]; for (size_t i = 0; i < resolverthreads.count; i++)
resolverstop(rt, true); resolverstop(i, true);
} }
SDL_UnlockMutex(resolvermutex);
} }
void void
resolverquery(char *name) resolverquery(OFString *name)
{ {
SDL_LockMutex(resolvermutex); @synchronized(ResolverThread.class) {
resolverqueries.add(name); [resolverqueries addObject:name];
SDL_SemPost(resolversem); SDL_SemPost(resolversem);
SDL_UnlockMutex(resolvermutex); }
} }
bool bool
resolvercheck(char **name, ENetAddress *address) resolvercheck(OFString **name, ENetAddress *address)
{ {
SDL_LockMutex(resolvermutex); @synchronized(ResolverThread.class) {
if (!resolverresults.empty()) { if (resolverresults.count > 0) {
resolverresult &rr = resolverresults.pop(); ResolverResult *rr = resolverresults.lastObject;
*name = rr.query; *name = rr.query;
*address = rr.address; *address = rr.address;
SDL_UnlockMutex(resolvermutex); [resolverresults
return true; removeObjectAtIndex:resolverresults.count - 1];
} return true;
loopv(resolverthreads) }
{
resolverthread &rt = resolverthreads[i]; for (size_t i = 0; i < resolverthreads.count; i++) {
if (rt.query) { ResolverThread *rt = resolverthreads[i];
if (lastmillis - rt.starttime > resolverlimit) {
resolverstop(rt, true); if (rt.query) {
*name = rt.query; if (lastmillis - rt.starttime > resolverlimit) {
SDL_UnlockMutex(resolvermutex); resolverstop(i, true);
return true; *name = rt.query;
return true;
}
} }
} }
} }
SDL_UnlockMutex(resolvermutex);
return false; return false;
} }
struct serverinfo { @interface ServerInfo: OFObject <OFComparing>
string name; @property (nonatomic) OFString *name;
string full; @property (nonatomic) OFString *full;
string map; @property (nonatomic) OFString *map;
string sdesc; @property (nonatomic) OFString *sdesc;
int mode, numplayers, ping, protocol, minremain; @property (nonatomic) int mode, numplayers, ping, protocol, minremain;
ENetAddress address; @property (nonatomic) ENetAddress address;
}; @end
vector<serverinfo> servers; @implementation ServerInfo
ENetSocket pingsock = ENET_SOCKET_NULL; - (OFComparisonResult)compare:(id)otherObject
int lastinfo = 0; {
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<ServerInfo *> *servers;
static ENetSocket pingsock = ENET_SOCKET_NULL;
static int lastinfo = 0;
OFString * OFString *
getservername(int n) getservername(int n)
{ {
@autoreleasepool { return servers[n].name;
return @(servers[n].name);
}
} }
void void
addserver(OFString *servername_) addserver(OFString *servername)
{ {
@autoreleasepool { @autoreleasepool {
const char *servername = servername_.UTF8String; for (ServerInfo *si in servers)
loopv(servers) if (strcmp(servers[i].name, servername) == if ([si.name isEqual:servername])
0) return; return;
serverinfo &si = servers.insert(0, serverinfo());
strcpy_s(si.name, servername); ServerInfo *si = [[ServerInfo alloc] init];
si.full[0] = 0; si.name = servername;
si.full = @"";
si.mode = 0; si.mode = 0;
si.numplayers = 0; si.numplayers = 0;
si.ping = 9999; si.ping = 9999;
si.protocol = 0; si.protocol = 0;
si.minremain = 0; si.minremain = 0;
si.map[0] = 0; si.map = @"";
si.sdesc[0] = 0; si.sdesc = @"";
si.address.host = ENET_HOST_ANY; ENetAddress address = { .host = ENET_HOST_ANY,
si.address.port = CUBE_SERVINFO_PORT; .port = CUBE_SERVINFO_PORT };
si.address = address;
if (servers == nil)
servers = [[OFMutableArray alloc] init];
[servers addObject:si];
} }
} }
@ -181,32 +236,33 @@ pingservers()
ENetBuffer buf; ENetBuffer buf;
uchar ping[MAXTRANS]; uchar ping[MAXTRANS];
uchar *p; uchar *p;
loopv(servers)
{ for (ServerInfo *si in servers) {
serverinfo &si = servers[i];
if (si.address.host == ENET_HOST_ANY) if (si.address.host == ENET_HOST_ANY)
continue; continue;
p = ping; p = ping;
putint(p, lastmillis); putint(p, lastmillis);
buf.data = ping; buf.data = ping;
buf.dataLength = p - 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; lastinfo = lastmillis;
} }
void void
checkresolver() checkresolver()
{ {
char *name = NULL; OFString *name = nil;
ENetAddress addr = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; ENetAddress addr = { ENET_HOST_ANY, CUBE_SERVINFO_PORT };
while (resolvercheck(&name, &addr)) { while (resolvercheck(&name, &addr)) {
if (addr.host == ENET_HOST_ANY) if (addr.host == ENET_HOST_ANY)
continue; continue;
loopv(servers)
{ for (ServerInfo *si in servers) {
serverinfo &si = servers[i]; if ([name isEqual:si.name]) {
if (name == si.name) {
si.address = addr; si.address = addr;
addr.host = ENET_HOST_ANY; addr.host = ENET_HOST_ANY;
break; break;
@ -225,12 +281,12 @@ checkpings()
char text[MAXTRANS]; char text[MAXTRANS];
buf.data = ping; buf.data = ping;
buf.dataLength = sizeof(ping); buf.dataLength = sizeof(ping);
while (enet_socket_wait(pingsock, &events, 0) >= 0 && events) { while (enet_socket_wait(pingsock, &events, 0) >= 0 && events) {
if (enet_socket_receive(pingsock, &addr, &buf, 1) <= 0) if (enet_socket_receive(pingsock, &addr, &buf, 1) <= 0)
return; return;
loopv(servers)
{ for (ServerInfo *si in servers) {
serverinfo &si = servers[i];
if (addr.host == si.address.host) { if (addr.host == si.address.host) {
p = ping; p = ping;
si.ping = lastmillis - getint(p); si.ping = lastmillis - getint(p);
@ -241,23 +297,19 @@ checkpings()
si.numplayers = getint(p); si.numplayers = getint(p);
si.minremain = getint(p); si.minremain = getint(p);
sgetstr(); sgetstr();
strcpy_s(si.map, text); @autoreleasepool {
si.map = @(text);
}
sgetstr(); sgetstr();
strcpy_s(si.sdesc, text); @autoreleasepool {
si.sdesc = @(text);
}
break; 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 void
refreshservers() refreshservers()
{ {
@ -265,37 +317,43 @@ refreshservers()
checkpings(); checkpings();
if (lastmillis - lastinfo >= 5000) if (lastmillis - lastinfo >= 5000)
pingservers(); pingservers();
servers.sort((void *)sicompare); [servers sort];
int maxmenu = 16; int maxmenu = 16;
loopv(servers)
{ size_t i = 0;
serverinfo &si = servers[i]; for (ServerInfo *si in servers) {
if (si.address.host != ENET_HOST_ANY && si.ping != 9999) { if (si.address.host != ENET_HOST_ANY && si.ping != 9999) {
if (si.protocol != PROTOCOL_VERSION) if (si.protocol != PROTOCOL_VERSION)
sprintf_s(si.full)( si.full = [[OFString alloc]
"%s [different cube protocol]", si.name); initWithFormat:
else { @"%@ [different cube protocol]",
@autoreleasepool { si.name];
sprintf_s(si.full)( else
"%d\t%d\t%s, %s: %s %s", si.ping, si.full = [[OFString alloc]
si.numplayers, initWithFormat:@"%d\t%d\t%@, %@: %@ %@",
si.map[0] ? si.map : "[unknown]", si.ping, si.numplayers,
modestr(si.mode).UTF8String, si.map.length > 0 ? si.map : @"[unknown]",
si.name, si.sdesc); modestr(si.mode), si.name, si.sdesc];
} } else
} si.full = [[OFString alloc]
} else { initWithFormat:
sprintf_s(si.full)(si.address.host != ENET_HOST_ANY (si.address.host != ENET_HOST_ANY
? "%s [waiting for server response]" ? @"%@ [waiting for server response]"
: "%s [unknown host]\t", : @"%@ [unknown host]\t"),
si.name); si.name];
}
si.full[50] = 0; // cut off too long server descriptions // cut off too long server descriptions
@autoreleasepool { @autoreleasepool {
menumanual(1, i, @(si.full)); if (si.full.length > 50)
si.full = [si.full substringToIndex:50];
} }
menumanual(1, i, si.full);
if (!--maxmenu) if (!--maxmenu)
return; return;
i++;
} }
} }
@ -306,8 +364,12 @@ servermenu()
pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL); pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL);
resolverinit(1, 1000); resolverinit(1, 1000);
} }
resolverclear(); resolverclear();
loopv(servers) resolverquery(servers[i].name);
for (ServerInfo *si in servers)
resolverquery(si.name);
refreshservers(); refreshservers();
menuset(1); menuset(1);
} }
@ -322,7 +384,7 @@ updatefrommaster()
strstr((char *)reply, "<HTML>")) strstr((char *)reply, "<HTML>"))
conoutf(@"master server not replying"); conoutf(@"master server not replying");
else { else {
servers.setsize(0); [servers removeAllObjects];
@autoreleasepool { @autoreleasepool {
execute(@((char *)reply)); execute(@((char *)reply));
} }
@ -341,6 +403,9 @@ writeservercfg()
if (!f) if (!f)
return; return;
fprintf(f, "// servers connected to are added here automatically\n\n"); 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); fclose(f);
} }