Index: src/Alias.h ================================================================== --- src/Alias.h +++ src/Alias.h @@ -4,10 +4,13 @@ @interface Alias: Identifier @property (copy, nonatomic) OFString *action; @property (readonly, nonatomic) bool persisted; ++ (instancetype)aliasWithName:(OFString *)name + action:(OFString *)action + persisted:(bool)persisted; - (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE; - (instancetype)initWithName:(OFString *)name action:(OFString *)action persisted:(bool)persisted; @end Index: src/Alias.m ================================================================== --- src/Alias.m +++ src/Alias.m @@ -1,8 +1,17 @@ #import "Alias.h" @implementation Alias ++ (instancetype)aliasWithName:(OFString *)name + action:(OFString *)action + persisted:(bool)persisted; +{ + return [[self alloc] initWithName:name + action:action + persisted:persisted]; +} + - (instancetype)initWithName:(OFString *)name action:(OFString *)action persisted:(bool)persisted { self = [super initWithName:name]; ADDED src/Client.h Index: src/Client.h ================================================================== --- /dev/null +++ src/Client.h @@ -0,0 +1,15 @@ +#import + +#import "cube.h" + +// 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; + ++ (instancetype)client; +@end ADDED src/Client.mm Index: src/Client.mm ================================================================== --- /dev/null +++ src/Client.mm @@ -0,0 +1,8 @@ +#import "Client.h" + +@implementation Client ++ (instancetype)client +{ + return [[self alloc] init]; +} +@end Index: src/Command.h ================================================================== --- src/Command.h +++ src/Command.h @@ -4,10 +4,13 @@ @interface Command: Identifier @property (readonly, nonatomic) void (*function)(); @property (readonly, nonatomic) int argumentsTypes; ++ (instancetype)commandWithName:(OFString *)name + function:(void (*)())function + argumentsTypes:(int)argumentsTypes; - (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE; - (instancetype)initWithName:(OFString *)name function:(void (*)())function argumentsTypes:(int)argumentsTypes; - (int)callWithArguments:(OFArray *)arguments isDown:(bool)isDown; Index: src/Command.mm ================================================================== --- src/Command.mm +++ src/Command.mm @@ -18,10 +18,19 @@ [copy makeImmutable]; return copy; } @implementation Command ++ (instancetype)commandWithName:(OFString *)name + function:(void (*)())function + argumentsTypes:(int)argumentsTypes +{ + return [[self alloc] initWithName:name + function:function + argumentsTypes:argumentsTypes]; +} + - (instancetype)initWithName:(OFString *)name function:(void (*)())function argumentsTypes:(int)argumentsTypes { self = [super initWithName:name]; ADDED src/ConsoleLine.h Index: src/ConsoleLine.h ================================================================== --- /dev/null +++ src/ConsoleLine.h @@ -0,0 +1,9 @@ +#import + +@interface ConsoleLine: OFObject +@property (readonly, copy) OFString *text; +@property (readonly) int outtime; + ++ (instancetype)lineWithText:(OFString *)text outtime:(int)outtime; +- (instancetype)initWithText:(OFString *)text outtime:(int)outtime; +@end ADDED src/ConsoleLine.m Index: src/ConsoleLine.m ================================================================== --- /dev/null +++ src/ConsoleLine.m @@ -0,0 +1,18 @@ +#import "ConsoleLine.h" + +@implementation ConsoleLine ++ (instancetype)lineWithText:(OFString *)text outtime:(int)outtime +{ + return [[self alloc] initWithText:text outtime:outtime]; +} + +- (instancetype)initWithText:(OFString *)text outtime:(int)outtime +{ + self = [super init]; + + _text = [text copy]; + _outtime = outtime; + + return self; +} +@end Index: src/KeyMapping.h ================================================================== --- src/KeyMapping.h +++ src/KeyMapping.h @@ -5,9 +5,11 @@ @interface KeyMapping: OFObject @property (readonly) int code; @property (readonly, nonatomic) OFString *name; @property (copy, nonatomic) OFString *action; ++ (instancetype)mappingWithCode:(int)code name:(OFString *)name; +- (instancetype)init OF_UNAVAILABLE; - (instancetype)initWithCode:(int)code name:(OFString *)name; @end OF_ASSUME_NONNULL_END Index: src/KeyMapping.m ================================================================== --- src/KeyMapping.m +++ src/KeyMapping.m @@ -1,8 +1,13 @@ #import "KeyMapping.h" @implementation KeyMapping ++ (instancetype)mappingWithCode:(int)code name:(OFString *)name +{ + return [[self alloc] initWithCode:code name:name]; +} + - (instancetype)initWithCode:(int)code name:(OFString *)name { self = [super init]; _code = code; Index: src/MD2.h ================================================================== --- src/MD2.h +++ src/MD2.h @@ -8,17 +8,16 @@ @property (nonatomic) MapModelInfo *mmi; @property (copy, nonatomic) OFString *loadname; @property (nonatomic) int mdlnum; @property (nonatomic) bool loaded; ++ (instancetype)md2; - (bool)loadWithIRI:(OFIRI *)IRI; - (void)renderWithLight:(OFVector3D)light frame:(int)frame range:(int)range - x:(float)x - y:(float)y - z:(float)z + position:(OFVector3D)position yaw:(float)yaw pitch:(float)pitch scale:(float)scale speed:(float)speed snap:(int)snap Index: src/MD2.mm ================================================================== --- src/MD2.mm +++ src/MD2.mm @@ -41,10 +41,15 @@ char *_frames; OFVector3D **_mverts; int _displaylist; int _displaylistverts; } + ++ (instancetype)md2 +{ + return [[self alloc] init]; +} - (void)dealloc { if (_glCommands) delete[] _glCommands; @@ -124,13 +129,11 @@ } - (void)renderWithLight:(OFVector3D)light frame:(int)frame range:(int)range - x:(float)x - y:(float)y - z:(float)z + position:(OFVector3D)position yaw:(float)yaw pitch:(float)pitch scale:(float)sc speed:(float)speed snap:(int)sn @@ -139,11 +142,11 @@ loopi(range) if (!_mverts[frame + i])[self scaleWithFrame:frame + i scale:sc snap:sn]; glPushMatrix(); - glTranslatef(x, y, z); + glTranslatef(position.x, position.y, position.z); glRotatef(yaw + 180, 0, -1, 0); glRotatef(pitch, 0, 0, 1); glColor3fv((float *)&light); Index: src/MapModelInfo.h ================================================================== --- src/MapModelInfo.h +++ src/MapModelInfo.h @@ -4,10 +4,16 @@ @interface MapModelInfo: OFObject @property (nonatomic) int rad, h, zoff, snap; @property (copy, nonatomic) OFString *name; ++ (instancetype)infoWithRad:(int)rad + h:(int)h + zoff:(int)zoff + snap:(int)snap + name:(OFString *)name; +- (instancetype)init OF_UNAVAILABLE; - (instancetype)initWithRad:(int)rad h:(int)h zoff:(int)zoff snap:(int)snap name:(OFString *)name; Index: src/MapModelInfo.m ================================================================== --- src/MapModelInfo.m +++ src/MapModelInfo.m @@ -1,8 +1,17 @@ #import "MapModelInfo.h" @implementation MapModelInfo ++ (instancetype)infoWithRad:(int)rad + h:(int)h + zoff:(int)zoff + snap:(int)snap + name:(OFString *)name +{ + return [[self alloc] initWithRad:rad h:h zoff:zoff snap:snap name:name]; +} + - (instancetype)initWithRad:(int)rad h:(int)h zoff:(int)zoff snap:(int)snap name:(OFString *)name Index: src/Menu.h ================================================================== --- src/Menu.h +++ src/Menu.h @@ -8,9 +8,11 @@ @property (readonly, nonatomic) OFString *name; @property (readonly) OFMutableArray *items; @property (nonatomic) int mwidth; @property (nonatomic) int menusel; ++ (instancetype)menuWithName:(OFString *)name; +- (instancetype)init OF_UNAVAILABLE; - (instancetype)initWithName:(OFString *)name; @end OF_ASSUME_NONNULL_END Index: src/Menu.m ================================================================== --- src/Menu.m +++ src/Menu.m @@ -1,8 +1,13 @@ #import "Menu.h" @implementation Menu ++ (instancetype)menuWithName:(OFString *)name +{ + return [[self alloc] initWithName:name]; +} + - (instancetype)initWithName:(OFString *)name { self = [super init]; _name = [name copy]; Index: src/MenuItem.h ================================================================== --- src/MenuItem.h +++ src/MenuItem.h @@ -1,7 +1,9 @@ #import @interface MenuItem: OFObject @property (readonly, nonatomic) OFString *text, *action; ++ (instancetype)itemWithText:(OFString *)text action:(OFString *)action; +- (instancetype)init OF_UNAVAILABLE; - (instancetype)initWithText:(OFString *)text action:(OFString *)action; @end Index: src/MenuItem.m ================================================================== --- src/MenuItem.m +++ src/MenuItem.m @@ -1,8 +1,13 @@ #import "MenuItem.h" @implementation MenuItem ++ (instancetype)itemWithText:(OFString *)text action:(OFString *)action +{ + return [[self alloc] initWithText:text action:action]; +} + - (instancetype)initWithText:(OFString *)text action:(OFString *)action { self = [super init]; _text = [text copy]; Index: src/Projectile.h ================================================================== --- src/Projectile.h +++ src/Projectile.h @@ -6,6 +6,8 @@ @property (nonatomic) OFVector3D o, to; @property (nonatomic) float speed; @property (nonatomic) DynamicEntity *owner; @property (nonatomic) int gun; @property (nonatomic) bool inuse, local; + ++ (instancetype)projectile; @end Index: src/Projectile.m ================================================================== --- src/Projectile.m +++ src/Projectile.m @@ -1,4 +1,8 @@ #import "Projectile.h" @implementation Projectile ++ (instancetype)projectile +{ + return [[self alloc] init]; +} @end ADDED src/ResolverResult.h Index: src/ResolverResult.h ================================================================== --- /dev/null +++ src/ResolverResult.h @@ -0,0 +1,12 @@ +#import + +#import "cube.h" + +@interface ResolverResult: OFObject +@property (readonly, nonatomic) OFString *query; +@property (readonly, nonatomic) ENetAddress address; + ++ (instancetype)resultWithQuery:(OFString *)query address:(ENetAddress)address; +- (instancetype)init OF_UNAVAILABLE; +- (instancetype)initWithQuery:(OFString *)query address:(ENetAddress)address; +@end ADDED src/ResolverResult.mm Index: src/ResolverResult.mm ================================================================== --- /dev/null +++ src/ResolverResult.mm @@ -0,0 +1,18 @@ +#import "ResolverResult.h" + +@implementation ResolverResult ++ (instancetype)resultWithQuery:(OFString *)query address:(ENetAddress)address +{ + return [[self alloc] initWithQuery:query address:address]; +} + +- (instancetype)initWithQuery:(OFString *)query address:(ENetAddress)address +{ + self = [super init]; + + _query = query; + _address = address; + + return self; +} +@end ADDED src/ResolverThread.h Index: src/ResolverThread.h ================================================================== --- /dev/null +++ src/ResolverThread.h @@ -0,0 +1,12 @@ +#import + +@interface ResolverThread: OFThread +{ + volatile bool _stop; +} + +@property (copy, nonatomic) OFString *query; +@property (nonatomic) int starttime; + +- (void)stop; +@end ADDED src/ResolverThread.mm Index: src/ResolverThread.mm ================================================================== --- /dev/null +++ src/ResolverThread.mm @@ -0,0 +1,44 @@ +#import "ResolverThread.h" + +#import "ResolverResult.h" + +extern SDL_sem *resolversem; +extern OFMutableArray *resolverqueries; +extern OFMutableArray *resolverresults; + +@implementation ResolverThread +- (id)main +{ + while (!_stop) { + SDL_SemWait(resolversem); + + @synchronized(ResolverThread.class) { + if (resolverqueries.count == 0) + continue; + + _query = resolverqueries.lastObject; + [resolverqueries removeLastObject]; + _starttime = lastmillis; + } + + ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; + enet_address_set_host(&address, _query.UTF8String); + + @synchronized(ResolverThread.class) { + [resolverresults + addObject:[ResolverResult resultWithQuery:_query + address:address]]; + + _query = NULL; + _starttime = 0; + } + } + + return nil; +} + +- (void)stop +{ + _stop = true; +} +@end Index: src/ServerInfo.h ================================================================== --- src/ServerInfo.h +++ src/ServerInfo.h @@ -8,8 +8,9 @@ @property (copy, nonatomic) OFString *map; @property (copy, nonatomic) OFString *sdesc; @property (nonatomic) int mode, numplayers, ping, protocol, minremain; @property (nonatomic) ENetAddress address; ++ (instancetype)infoWithName:(OFString *)name; - (instancetype)init OF_UNAVAILABLE; - (instancetype)initWithName:(OFString *)name; @end Index: src/ServerInfo.mm ================================================================== --- src/ServerInfo.mm +++ src/ServerInfo.mm @@ -1,10 +1,15 @@ #import "ServerInfo.h" #include "cube.h" @implementation ServerInfo ++ (instancetype)infoWithName:(OFString *)name; +{ + return [[self alloc] initWithName:name]; +} + - (instancetype)initWithName:(OFString *)name { self = [super init]; _name = [name copy]; Index: src/Variable.h ================================================================== --- src/Variable.h +++ src/Variable.h @@ -6,10 +6,16 @@ @property (readonly, nonatomic) int min, max; @property (readonly, nonatomic) int *storage; @property (readonly, nonatomic) void (*__cdecl function)(); @property (readonly, nonatomic) bool persisted; ++ (instancetype)variableWithName:(OFString *)name + min:(int)min + max:(int)max + storage:(int *)storage + function:(void (*__cdecl)())function + persisted:(bool)persisted; - (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE; - (instancetype)initWithName:(OFString *)name min:(int)min max:(int)max storage:(int *)storage Index: src/Variable.mm ================================================================== --- src/Variable.mm +++ src/Variable.mm @@ -1,10 +1,25 @@ #import "Variable.h" #include "cube.h" @implementation Variable ++ (instancetype)variableWithName:(OFString *)name + min:(int)min + max:(int)max + storage:(int *)storage + function:(void (*__cdecl)())function + persisted:(bool)persisted +{ + return [[self alloc] initWithName:name + min:min + max:max + storage:storage + function:function + persisted:persisted]; +} + - (instancetype)initWithName:(OFString *)name min:(int)min max:(int)max storage:(int *)storage function:(void (*__cdecl)())function DELETED src/client.mm Index: src/client.mm ================================================================== --- src/client.mm +++ /dev/null @@ -1,414 +0,0 @@ -// client.cpp, mostly network related client game code - -#include "cube.h" - -#import "DynamicEntity.h" - -static ENetHost *clienthost = NULL; -static int connecting = 0; -static int connattempts = 0; -static int disconnecting = 0; -// our client id in the game -int clientnum = -1; -// whether we need to tell the other clients our stats -bool c2sinit = false; - -int -getclientnum() -{ - return clientnum; -} - -bool -multiplayer() -{ - // check not correct on listen server? - if (clienthost) - conoutf(@"operation not available in multiplayer"); - - return clienthost != NULL; -} - -bool -allowedittoggle() -{ - bool allow = !clienthost || gamemode == 1; - - if (!allow) - conoutf(@"editing in multiplayer requires coopedit mode (1)"); - - return allow; -} - -VARF(rate, 0, 0, 25000, - if (clienthost && (!rate || rate > 1000)) - enet_host_bandwidth_limit(clienthost, rate, rate)); - -void throttle(); - -VARF(throttle_interval, 0, 5, 30, throttle()); -VARF(throttle_accel, 0, 2, 32, throttle()); -VARF(throttle_decel, 0, 2, 32, throttle()); - -void -throttle() -{ - if (!clienthost || connecting) - return; - assert(ENET_PEER_PACKET_THROTTLE_SCALE == 32); - enet_peer_throttle_configure(clienthost->peers, - throttle_interval * 1000, throttle_accel, throttle_decel); -} - -void -newname(OFString *name) -{ - c2sinit = false; - - if (name.length > 16) - name = [name substringToIndex:16]; - - player1.name = name; -} -COMMANDN(name, newname, ARG_1STR) - -void -newteam(OFString *name) -{ - c2sinit = false; - - if (name.length > 5) - name = [name substringToIndex:5]; - - player1.team = name; -} -COMMANDN(team, newteam, ARG_1STR) - -void -writeclientinfo(OFStream *stream) -{ - [stream writeFormat:@"name \"%@\"\nteam \"%@\"\n", player1.name, - player1.team]; -} - -void -connects(OFString *servername) -{ - disconnect(1); // reset state - addserver(servername); - - conoutf(@"attempting to connect to %@", servername); - ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; - if (enet_address_set_host(&address, servername.UTF8String) < 0) { - conoutf(@"could not resolve server %@", servername); - return; - } - - clienthost = enet_host_create(NULL, 1, rate, rate); - - if (clienthost) { - enet_host_connect(clienthost, &address, 1); - enet_host_flush(clienthost); - connecting = lastmillis; - connattempts = 0; - } else { - conoutf(@"could not connect to server"); - disconnect(); - } -} - -void -disconnect(int onlyclean, int async) -{ - if (clienthost) { - if (!connecting && !disconnecting) { - enet_peer_disconnect(clienthost->peers); - enet_host_flush(clienthost); - disconnecting = lastmillis; - } - if (clienthost->peers->state != ENET_PEER_STATE_DISCONNECTED) { - if (async) - return; - enet_peer_reset(clienthost->peers); - } - enet_host_destroy(clienthost); - } - - if (clienthost && !connecting) - conoutf(@"disconnected"); - clienthost = NULL; - connecting = 0; - connattempts = 0; - disconnecting = 0; - clientnum = -1; - c2sinit = false; - player1.lifesequence = 0; - [players removeAllObjects]; - - localdisconnect(); - - if (!onlyclean) { - stop(); - localconnect(); - } -} - -void -trydisconnect() -{ - if (!clienthost) { - conoutf(@"not connected"); - return; - } - if (connecting) { - conoutf(@"aborting connection attempt"); - disconnect(); - return; - } - conoutf(@"attempting to disconnect..."); - disconnect(0, !disconnecting); -} - -static OFString *ctext; -void -toserver(OFString *text) -{ - conoutf(@"%@:\f %@", player1.name, text); - ctext = text; -} - -void -echo(OFString *text) -{ - conoutf(@"%@", text); -} - -COMMAND(echo, ARG_VARI) -COMMANDN(say, toserver, ARG_VARI) -COMMANDN(connect, connects, ARG_1STR) -COMMANDN(disconnect, trydisconnect, ARG_NONE) - -// collect c2s messages conveniently - -static OFMutableArray *messages; - -void -addmsg(int rel, int num, int type, ...) -{ - if (demoplayback) - return; - if (num != msgsizelookup(type)) { - fatal([OFString - stringWithFormat:@"inconsistant msg size for %d (%d != %d)", - type, num, msgsizelookup(type)]); - } - if (messages.count == 100) { - conoutf(@"command flood protection (type %d)", type); - return; - } - - OFMutableData *msg = [OFMutableData dataWithItemSize:sizeof(int) - capacity:num + 2]; - [msg addItem:&num]; - [msg addItem:&rel]; - [msg addItem:&type]; - - va_list marker; - va_start(marker, type); - loopi(num - 1) - { - int tmp = va_arg(marker, int); - [msg addItem:&tmp]; - } - va_end(marker); - [msg makeImmutable]; - - if (messages == nil) - messages = [[OFMutableArray alloc] init]; - - [messages addObject:msg]; -} - -void -server_err() -{ - conoutf(@"server network error, disconnecting..."); - disconnect(); -} - -int lastupdate = 0, lastping = 0; -OFString *toservermap; -bool senditemstoserver = - false; // after a map change, since server doesn't have map data - -OFString *clientpassword; -void -password(OFString *p) -{ - clientpassword = p; -} -COMMAND(password, ARG_1STR) - -bool -netmapstart() -{ - senditemstoserver = true; - return clienthost != NULL; -} - -void -initclientnet() -{ - ctext = @""; - toservermap = @""; - clientpassword = @""; - newname(@"unnamed"); - newteam(@"red"); -} - -void -sendpackettoserv(void *packet) -{ - if (clienthost) { - enet_host_broadcast(clienthost, 0, (ENetPacket *)packet); - enet_host_flush(clienthost); - } else - localclienttoserver((ENetPacket *)packet); -} - -// send update to the server -void -c2sinfo(DynamicEntity *d) -{ - 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; - // suggest server to change map - if (toservermap.length > 0) { - // 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 = @""; - putint(p, nextmode); - } else { - putint(p, SV_POS); - putint(p, clientnum); - // quantize coordinates to 1/16th of a cube, between 1 and 3 - // bytes - putint(p, (int)(d.o.x * DMF)); - 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)); - // quantize to 1/100, almost always 1 byte - putint(p, (int)(d.vel.x * DVF)); - 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; - } - // player chat, not flood protected for now - if (ctext.length > 0) { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_TEXT); - sendstring(ctext, p); - ctext = @""; - } - // tell other clients who I am - if (!c2sinit) { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - c2sinit = true; - putint(p, SV_INITC2S); - sendstring(player1.name, p); - sendstring(player1.team, p); - putint(p, player1.lifesequence); - } - for (OFData *msg in messages) { - // send messages collected during the previous frames - if (*(int *)[msg itemAtIndex:1]) - packet->flags = ENET_PACKET_FLAG_RELIABLE; - loopi(*(int *)[msg itemAtIndex:0]) - putint(p, *(int *)[msg itemAtIndex:i + 2]); - } - [messages removeAllObjects]; - 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; - if (!clienthost) - return; - if (connecting && lastmillis / 3000 > connecting / 3000) { - conoutf(@"attempting to connect..."); - connecting = lastmillis; - ++connattempts; - if (connattempts > 3) { - conoutf(@"could not connect to server"); - disconnect(); - return; - } - } - while ( - clienthost != NULL && enet_host_service(clienthost, &event, 0) > 0) - switch (event.type) { - case ENET_EVENT_TYPE_CONNECT: - conoutf(@"connected to server"); - connecting = 0; - throttle(); - break; - - case ENET_EVENT_TYPE_RECEIVE: - if (disconnecting) - conoutf(@"attempting to disconnect..."); - else - localservertoclient(event.packet->data, - event.packet->dataLength); - enet_packet_destroy(event.packet); - break; - - case ENET_EVENT_TYPE_DISCONNECT: - if (disconnecting) - disconnect(); - else - server_err(); - return; - } -} Index: src/clientextras.mm ================================================================== --- src/clientextras.mm +++ src/clientextras.mm @@ -69,12 +69,13 @@ if (hellpig) { n++; scale *= 32; mz -= 1.9f; } - rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d.o.x, mz, d.o.y, - d.yaw + 90, d.pitch / 2, team, scale, speed, 0, basetime); + rendermodel(mdlname, frame[n], range[n], 0, 1.5f, + OFMakeVector3D(d.o.x, mz, d.o.y), d.yaw + 90, d.pitch / 2, team, + scale, speed, 0, basetime); } extern int democlientnum; void @@ -159,11 +160,11 @@ for (id player in players) if (player != [OFNull null]) addteamscore(player); if (!demoplayback) addteamscore(player1); - OFMutableString *teamScores = [[OFMutableString alloc] init]; + OFMutableString *teamScores = [OFMutableString string]; for (size_t j = 0; j < teamsUsed; j++) [teamScores appendFormat:@"[ %@: %d ]", teamName[j], teamScore[j]]; menumanual(0, scoreLines.count, @""); menumanual(0, scoreLines.count + 1, teamScores); Index: src/clientgame.mm ================================================================== --- src/clientgame.mm +++ src/clientgame.mm @@ -469,12 +469,10 @@ { if (cn < 0 || cn >= MAXCLIENTS) { neterr(@"clientnum"); return nil; } - if (players == nil) - players = [[OFMutableArray alloc] init]; while (cn >= players.count) [players addObject:[OFNull null]]; return (players[cn] != [OFNull null] ? players[cn] : (players[cn] = newdynent())); } @@ -482,12 +480,10 @@ void setclient(int cn, id client) { if (cn < 0 || cn >= MAXCLIENTS) neterr(@"clientnum"); - if (players == nil) - players = [[OFMutableArray alloc] init]; while (cn >= players.count) [players addObject:[OFNull null]]; players[cn] = client; } ADDED src/clients.mm Index: src/clients.mm ================================================================== --- /dev/null +++ src/clients.mm @@ -0,0 +1,414 @@ +// client.cpp, mostly network related client game code + +#include "cube.h" + +#import "DynamicEntity.h" + +static ENetHost *clienthost = NULL; +static int connecting = 0; +static int connattempts = 0; +static int disconnecting = 0; +// our client id in the game +int clientnum = -1; +// whether we need to tell the other clients our stats +bool c2sinit = false; + +int +getclientnum() +{ + return clientnum; +} + +bool +multiplayer() +{ + // check not correct on listen server? + if (clienthost) + conoutf(@"operation not available in multiplayer"); + + return clienthost != NULL; +} + +bool +allowedittoggle() +{ + bool allow = !clienthost || gamemode == 1; + + if (!allow) + conoutf(@"editing in multiplayer requires coopedit mode (1)"); + + return allow; +} + +VARF(rate, 0, 0, 25000, + if (clienthost && (!rate || rate > 1000)) + enet_host_bandwidth_limit(clienthost, rate, rate)); + +void throttle(); + +VARF(throttle_interval, 0, 5, 30, throttle()); +VARF(throttle_accel, 0, 2, 32, throttle()); +VARF(throttle_decel, 0, 2, 32, throttle()); + +void +throttle() +{ + if (!clienthost || connecting) + return; + assert(ENET_PEER_PACKET_THROTTLE_SCALE == 32); + enet_peer_throttle_configure(clienthost->peers, + throttle_interval * 1000, throttle_accel, throttle_decel); +} + +void +newname(OFString *name) +{ + c2sinit = false; + + if (name.length > 16) + name = [name substringToIndex:16]; + + player1.name = name; +} +COMMANDN(name, newname, ARG_1STR) + +void +newteam(OFString *name) +{ + c2sinit = false; + + if (name.length > 5) + name = [name substringToIndex:5]; + + player1.team = name; +} +COMMANDN(team, newteam, ARG_1STR) + +void +writeclientinfo(OFStream *stream) +{ + [stream writeFormat:@"name \"%@\"\nteam \"%@\"\n", player1.name, + player1.team]; +} + +void +connects(OFString *servername) +{ + disconnect(1); // reset state + addserver(servername); + + conoutf(@"attempting to connect to %@", servername); + ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; + if (enet_address_set_host(&address, servername.UTF8String) < 0) { + conoutf(@"could not resolve server %@", servername); + return; + } + + clienthost = enet_host_create(NULL, 1, rate, rate); + + if (clienthost) { + enet_host_connect(clienthost, &address, 1); + enet_host_flush(clienthost); + connecting = lastmillis; + connattempts = 0; + } else { + conoutf(@"could not connect to server"); + disconnect(); + } +} + +void +disconnect(int onlyclean, int async) +{ + if (clienthost) { + if (!connecting && !disconnecting) { + enet_peer_disconnect(clienthost->peers); + enet_host_flush(clienthost); + disconnecting = lastmillis; + } + if (clienthost->peers->state != ENET_PEER_STATE_DISCONNECTED) { + if (async) + return; + enet_peer_reset(clienthost->peers); + } + enet_host_destroy(clienthost); + } + + if (clienthost && !connecting) + conoutf(@"disconnected"); + clienthost = NULL; + connecting = 0; + connattempts = 0; + disconnecting = 0; + clientnum = -1; + c2sinit = false; + player1.lifesequence = 0; + [players removeAllObjects]; + + localdisconnect(); + + if (!onlyclean) { + stop(); + localconnect(); + } +} + +void +trydisconnect() +{ + if (!clienthost) { + conoutf(@"not connected"); + return; + } + if (connecting) { + conoutf(@"aborting connection attempt"); + disconnect(); + return; + } + conoutf(@"attempting to disconnect..."); + disconnect(0, !disconnecting); +} + +static OFString *ctext; +void +toserver(OFString *text) +{ + conoutf(@"%@:\f %@", player1.name, text); + ctext = text; +} + +void +echo(OFString *text) +{ + conoutf(@"%@", text); +} + +COMMAND(echo, ARG_VARI) +COMMANDN(say, toserver, ARG_VARI) +COMMANDN(connect, connects, ARG_1STR) +COMMANDN(disconnect, trydisconnect, ARG_NONE) + +// collect c2s messages conveniently + +static OFMutableArray *messages; + +void +addmsg(int rel, int num, int type, ...) +{ + if (demoplayback) + return; + if (num != msgsizelookup(type)) { + fatal([OFString + stringWithFormat:@"inconsistant msg size for %d (%d != %d)", + type, num, msgsizelookup(type)]); + } + if (messages.count == 100) { + conoutf(@"command flood protection (type %d)", type); + return; + } + + OFMutableData *msg = [OFMutableData dataWithItemSize:sizeof(int) + capacity:num + 2]; + [msg addItem:&num]; + [msg addItem:&rel]; + [msg addItem:&type]; + + va_list marker; + va_start(marker, type); + loopi(num - 1) + { + int tmp = va_arg(marker, int); + [msg addItem:&tmp]; + } + va_end(marker); + [msg makeImmutable]; + + if (messages == nil) + messages = [[OFMutableArray alloc] init]; + + [messages addObject:msg]; +} + +void +server_err() +{ + conoutf(@"server network error, disconnecting..."); + disconnect(); +} + +int lastupdate = 0, lastping = 0; +OFString *toservermap; +bool senditemstoserver = + false; // after a map change, since server doesn't have map data + +OFString *clientpassword; +void +password(OFString *p) +{ + clientpassword = p; +} +COMMAND(password, ARG_1STR) + +bool +netmapstart() +{ + senditemstoserver = true; + return clienthost != NULL; +} + +void +initclientnet() +{ + ctext = @""; + toservermap = @""; + clientpassword = @""; + newname(@"unnamed"); + newteam(@"red"); +} + +void +sendpackettoserv(void *packet) +{ + if (clienthost) { + enet_host_broadcast(clienthost, 0, (ENetPacket *)packet); + enet_host_flush(clienthost); + } else + localclienttoserver((ENetPacket *)packet); +} + +// send update to the server +void +c2sinfo(DynamicEntity *d) +{ + 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; + // suggest server to change map + if (toservermap.length > 0) { + // 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 = @""; + putint(p, nextmode); + } else { + putint(p, SV_POS); + putint(p, clientnum); + // quantize coordinates to 1/16th of a cube, between 1 and 3 + // bytes + putint(p, (int)(d.o.x * DMF)); + 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)); + // quantize to 1/100, almost always 1 byte + putint(p, (int)(d.vel.x * DVF)); + 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; + } + // player chat, not flood protected for now + if (ctext.length > 0) { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_TEXT); + sendstring(ctext, p); + ctext = @""; + } + // tell other clients who I am + if (!c2sinit) { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + c2sinit = true; + putint(p, SV_INITC2S); + sendstring(player1.name, p); + sendstring(player1.team, p); + putint(p, player1.lifesequence); + } + for (OFData *msg in messages) { + // send messages collected during the previous frames + if (*(int *)[msg itemAtIndex:1]) + packet->flags = ENET_PACKET_FLAG_RELIABLE; + loopi(*(int *)[msg itemAtIndex:0]) + putint(p, *(int *)[msg itemAtIndex:i + 2]); + } + [messages removeAllObjects]; + 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; + if (!clienthost) + return; + if (connecting && lastmillis / 3000 > connecting / 3000) { + conoutf(@"attempting to connect..."); + connecting = lastmillis; + ++connattempts; + if (connattempts > 3) { + conoutf(@"could not connect to server"); + disconnect(); + return; + } + } + while ( + clienthost != NULL && enet_host_service(clienthost, &event, 0) > 0) + switch (event.type) { + case ENET_EVENT_TYPE_CONNECT: + conoutf(@"connected to server"); + connecting = 0; + throttle(); + break; + + case ENET_EVENT_TYPE_RECEIVE: + if (disconnecting) + conoutf(@"attempting to disconnect..."); + else + localservertoclient(event.packet->data, + event.packet->dataLength); + enet_packet_destroy(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + if (disconnecting) + disconnect(); + else + server_err(); + return; + } +} Index: src/commands.mm ================================================================== --- src/commands.mm +++ src/commands.mm @@ -18,13 +18,11 @@ alias(OFString *name, OFString *action) { Alias *alias = identifiers[name]; if (alias == nil) { - alias = [[Alias alloc] initWithName:name - action:action - persisted:true]; + alias = [Alias aliasWithName:name action:action persisted:true]; if (identifiers == nil) identifiers = [[OFMutableDictionary alloc] init]; identifiers[name] = alias; @@ -40,16 +38,16 @@ int variable(OFString *name, int min, int cur, int max, int *storage, void (*function)(), bool persisted) { - Variable *variable = [[Variable alloc] initWithName:name - min:min - max:max - storage:storage - function:function - persisted:persisted]; + Variable *variable = [Variable variableWithName:name + min:min + max:max + storage:storage + function:function + persisted:persisted]; if (identifiers == nil) identifiers = [[OFMutableDictionary alloc] init]; identifiers[name] = variable; @@ -87,13 +85,13 @@ } bool addcommand(OFString *name, void (*function)(), int argumentsTypes) { - Command *command = [[Command alloc] initWithName:name - function:function - argumentsTypes:argumentsTypes]; + Command *command = [Command commandWithName:name + function:function + argumentsTypes:argumentsTypes]; if (identifiers == nil) identifiers = [[OFMutableDictionary alloc] init]; identifiers[name] = command; Index: src/console.mm ================================================================== --- src/console.mm +++ src/console.mm @@ -2,39 +2,16 @@ #include "cube.h" #include +#import "ConsoleLine.h" #import "KeyMapping.h" #import "OFString+Cube.h" -@interface ConsoleLine: OFObject -@property (readonly, copy) OFString *text; -@property (readonly) int outtime; - -- (instancetype)initWithText:(OFString *)text outtime:(int)outtime; -@end - static OFMutableArray *conlines; -@implementation ConsoleLine -- (instancetype)initWithText:(OFString *)text outtime:(int)outtime -{ - self = [super init]; - - _text = [text copy]; - _outtime = outtime; - - return self; -} - -- (OFString *)description -{ - return _text; -} -@end - const int ndraw = 5; const int WORDWRAP = 80; int conskip = 0; bool saycommandon = false; @@ -57,11 +34,11 @@ // constrain the buffer size if (conlines.count > 100) { text = [conlines.lastObject.text mutableCopy]; [conlines removeLastObject]; } else - text = [[OFMutableString alloc] init]; + text = [OFMutableString string]; if (highlight) // show line in a different colour, for chat etc. [text appendString:@"\f"]; @@ -68,12 +45,12 @@ [text appendString:sf]; if (conlines == nil) conlines = [[OFMutableArray alloc] init]; - [conlines insertObject:[[ConsoleLine alloc] initWithText:text - outtime:lastmillis] + [conlines insertObject:[ConsoleLine lineWithText:text + outtime:lastmillis] atIndex:0]; puts(text.UTF8String); #ifndef OF_WINDOWS fflush(stdout); @@ -133,12 +110,12 @@ keymap(OFString *code, OFString *key, OFString *action) { if (keyMappings == nil) keyMappings = [[OFMutableArray alloc] init]; - KeyMapping *mapping = - [[KeyMapping alloc] initWithCode:code.cube_intValue name:key]; + KeyMapping *mapping = [KeyMapping mappingWithCode:code.cube_intValue + name:key]; mapping.action = action; [keyMappings addObject:mapping]; } COMMAND(keymap, ARG_3STR) Index: src/entities.mm ================================================================== --- src/entities.mm +++ src/entities.mm @@ -24,12 +24,13 @@ void renderent(entity &e, OFString *mdlname, float z, float yaw, int frame = 0, int numf = 1, int basetime = 0, float speed = 10.0f) { - rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z + S(e.x, e.y)->floor, - e.y, yaw, 0, false, 1.0f, speed, 0, basetime); + rendermodel(mdlname, frame, numf, 0, 1.1f, + OFMakeVector3D(e.x, z + S(e.x, e.y)->floor, e.y), yaw, 0, false, + 1.0f, speed, 0, basetime); } void renderentities() { @@ -41,12 +42,14 @@ if (e.type == MAPMODEL) { MapModelInfo *mmi = getmminfo(e.attr2); if (mmi == nil) continue; rendermodel(mmi.name, 0, 1, e.attr4, (float)mmi.rad, - e.x, (float)S(e.x, e.y)->floor + mmi.zoff + e.attr3, - e.y, (float)((e.attr1 + 7) - (e.attr1 + 7) % 15), 0, + OFMakeVector3D(e.x, + (float)S(e.x, e.y)->floor + mmi.zoff + e.attr3, + e.y), + (float)((e.attr1 + 7) - (e.attr1 + 7) % 15), 0, false, 1.0f, 10.0f, mmi.snap); } else { if (OUTBORD(e.x, e.y)) continue; if (e.type != CARROT) { Index: src/menus.mm ================================================================== --- src/menus.mm +++ src/menus.mm @@ -93,32 +93,32 @@ newmenu(OFString *name) { if (menus == nil) menus = [[OFMutableArray alloc] init]; - [menus addObject:[[Menu alloc] initWithName:name]]; + [menus addObject:[Menu menuWithName:name]]; } COMMAND(newmenu, ARG_1STR) void menumanual(int m, int n, OFString *text) { if (n == 0) [menus[m].items removeAllObjects]; - MenuItem *item = [[MenuItem alloc] initWithText:text action:@""]; + MenuItem *item = [MenuItem itemWithText:text action:@""]; [menus[m].items addObject:item]; } void menuitem(OFString *text, OFString *action) { Menu *menu = menus.lastObject; MenuItem *item = - [[MenuItem alloc] initWithText:text - action:(action.length > 0 ? action : text)]; + [MenuItem itemWithText:text + action:(action.length > 0 ? action : text)]; [menu.items addObject:item]; } COMMAND(menuitem, ARG_2STR) bool Index: src/meson.build ================================================================== --- src/meson.build +++ src/meson.build @@ -1,9 +1,11 @@ executable('client', [ 'Alias.m', + 'Client.mm', 'Command.mm', + 'ConsoleLine.m', 'Cube.mm', 'DynamicEntity.mm', 'Identifier.m', 'KeyMapping.m', 'MD2.mm', @@ -10,13 +12,15 @@ 'MapModelInfo.m', 'Menu.m', 'MenuItem.m', 'OFString+Cube.mm', 'Projectile.m', + 'ResolverResult.mm', + 'ResolverThread.mm', 'ServerInfo.mm', 'Variable.mm', - 'client.mm', + 'clients.mm', 'clientextras.mm', 'clientgame.mm', 'clients2c.mm', 'commands.mm', 'console.mm', @@ -60,10 +64,11 @@ link_with: [enet], win_subsystem: 'windows') executable('server', [ + 'Client.mm', 'server.mm', 'serverms.mm', 'serverutil.mm', 'tools.mm', ], Index: src/protos.h ================================================================== --- src/protos.h +++ src/protos.h @@ -208,12 +208,12 @@ extern void initsound(); extern void cleansound(); // rendermd2 extern void rendermodel(OFString *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 = 0, int basetime = 0); + OFVector3D position, float yaw, float pitch, bool teammate, float scale, + float speed, int snap = 0, int basetime = 0); @class MapModelInfo; extern MapModelInfo *getmminfo(int i); // server extern void initserver(bool dedicated, int uprate, OFString *sdesc, Index: src/renderextras.mm ================================================================== --- src/renderextras.mm +++ src/renderextras.mm @@ -190,15 +190,15 @@ particle_splash(2, 2, 40, v); } int e = closestent(); if (e >= 0) { entity &c = ents[e]; - closeent = [[OFString alloc] - initWithFormat:@"closest entity = %@ (%d, %d, %d, %d), " - @"selection = (%d, %d)", - entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, - getvar(@"selxs"), getvar(@"selys")]; + closeent = + [OFString stringWithFormat:@"closest entity = %@ (%d, %d, " + @"%d, %d), selection = (%d, %d)", + entnames[c.type], c.attr1, c.attr2, c.attr3, + c.attr4, getvar(@"selxs"), getvar(@"selys")]; } } void loadsky(OFString *basename) Index: src/rendergl.mm ================================================================== --- src/rendergl.mm +++ src/rendergl.mm @@ -357,12 +357,12 @@ void drawhudmodel(int start, int end, float speed, int base) { rendermodel(hudgunnames[player1.gunselect], start, end, 0, 1.0f, - player1.o.x, player1.o.z, player1.o.y, player1.yaw + 90, - player1.pitch, false, 1.0f, speed, 0, base); + OFMakeVector3D(player1.o.x, player1.o.z, player1.o.y), + player1.yaw + 90, player1.pitch, false, 1.0f, speed, 0, base); } void drawhudgun(float fovy, float aspect, int farplane) { Index: src/rendermd2.mm ================================================================== --- src/rendermd2.mm +++ src/rendermd2.mm @@ -39,13 +39,13 @@ MD2 *m = mdllookup[name]; if (m != nil) return m; - m = [[MD2 alloc] init]; + m = [MD2 md2]; m.mdlnum = modelnum++; - m.mmi = [[MapModelInfo alloc] initWithRad:2 h:2 zoff:0 snap:0 name:@""]; + m.mmi = [MapModelInfo infoWithRad:2 h:2 zoff:0 snap:0 name:@""]; m.loadname = name; if (mdllookup == nil) mdllookup = [[OFMutableDictionary alloc] init]; @@ -58,15 +58,15 @@ mapmodel( OFString *rad, OFString *h, OFString *zoff, OFString *snap, OFString *name) { MD2 *m = loadmodel([name stringByReplacingOccurrencesOfString:@"\\" withString:@"/"]); - m.mmi = [[MapModelInfo alloc] initWithRad:rad.cube_intValue - h:h.cube_intValue - zoff:zoff.cube_intValue - snap:snap.cube_intValue - name:m.loadname]; + m.mmi = [MapModelInfo infoWithRad:rad.cube_intValue + h:h.cube_intValue + zoff:zoff.cube_intValue + snap:snap.cube_intValue + name:m.loadname]; if (mapmodels == nil) mapmodels = [[OFMutableArray alloc] init]; [mapmodels addObject:m]; @@ -85,27 +85,28 @@ { return i < mapmodels.count ? mapmodels[i].mmi : nil; } void -rendermodel(OFString *mdl, int frame, int range, int tex, float rad, float x, - float y, float z, float yaw, float pitch, bool teammate, float scale, +rendermodel(OFString *mdl, int frame, int range, int tex, float rad, + OFVector3D position, float yaw, float pitch, bool teammate, float scale, float speed, int snap, int basetime) { MD2 *m = loadmodel(mdl); - if (isoccluded(player1.o.x, player1.o.y, x - rad, z - rad, rad * 2)) + if (isoccluded(player1.o.x, player1.o.y, position.x - rad, + position.z - rad, rad * 2)) return; delayedload(m); int xs, ys; glBindTexture(GL_TEXTURE_2D, tex ? lookuptexture(tex, &xs, &ys) : FIRSTMDL + m.mdlnum); - int ix = (int)x; - int iy = (int)z; + int ix = (int)position.x; + int iy = (int)position.z; OFVector3D light = OFMakeVector3D(1, 1, 1); if (!OUTBORD(ix, iy)) { sqr *s = S(ix, iy); float ll = 256.0f; // 0.96f; @@ -122,15 +123,13 @@ } [m renderWithLight:light frame:frame range:range - x:x - y:y - z:z + position:position yaw:yaw pitch:pitch scale:scale speed:speed snap:snap basetime:basetime]; } Index: src/server.mm ================================================================== --- src/server.mm +++ src/server.mm @@ -1,25 +1,14 @@ // server.cpp: little more than enhanced multicaster // runs dedicated or as client coroutine #include "cube.h" + +#import "Client.h" enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; -// 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; @@ -357,11 +346,11 @@ { for (Client *client in clients) if (client.type == ST_EMPTY) return client; - Client *client = [[Client alloc] init]; + Client *client = [Client client]; if (clients == nil) clients = [[OFMutableArray alloc] init]; [clients addObject:client]; Index: src/serverbrowser.mm ================================================================== --- src/serverbrowser.mm +++ src/serverbrowser.mm @@ -2,84 +2,20 @@ // management #include "SDL_thread.h" #include "cube.h" +#import "ResolverResult.h" +#import "ResolverThread.h" #import "ServerInfo.h" -@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; +OFMutableArray *resolverqueries; +OFMutableArray *resolverresults; +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 removeLastObject]; - _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 resolverinit(int threads, int limit) { resolverthreads = [[OFMutableArray alloc] init]; resolverqueries = [[OFMutableArray alloc] init]; @@ -86,11 +22,11 @@ resolverresults = [[OFMutableArray alloc] init]; resolverlimit = limit; resolversem = SDL_CreateSemaphore(0); while (threads > 0) { - ResolverThread *rt = [[ResolverThread alloc] init]; + ResolverThread *rt = [ResolverThread thread]; rt.name = @"resolverthread"; [resolverthreads addObject:rt]; [rt start]; --threads; } @@ -102,11 +38,11 @@ @synchronized(ResolverThread.class) { ResolverThread *rt = resolverthreads[i]; [rt stop]; if (restart) { - rt = [[ResolverThread alloc] init]; + rt = [ResolverThread thread]; rt.name = @"resolverthread"; resolverthreads[i] = rt; [rt start]; @@ -185,11 +121,11 @@ return; if (servers == nil) servers = [[OFMutableArray alloc] init]; - [servers addObject:[[ServerInfo alloc] initWithName:servername]]; + [servers addObject:[ServerInfo infoWithName:servername]]; } void pingservers() { @@ -278,23 +214,23 @@ __block int maxmenu = 16; [servers enumerateObjectsUsingBlock:^( ServerInfo *si, size_t i, bool *stop) { if (si.address.host != ENET_HOST_ANY && si.ping != 9999) { if (si.protocol != PROTOCOL_VERSION) - si.full = [[OFString alloc] - initWithFormat: + si.full = [OFString + stringWithFormat: @"%@ [different cube protocol]", si.name]; else - si.full = [[OFString alloc] - initWithFormat:@"%d\t%d\t%@, %@: %@ %@", + si.full = [OFString + stringWithFormat:@"%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.full = [OFString + stringWithFormat: (si.address.host != ENET_HOST_ANY ? @"%@ [waiting for server response]" : @"%@ [unknown host]\t"), si.name]; Index: src/serverms.mm ================================================================== --- src/serverms.mm +++ src/serverms.mm @@ -156,12 +156,11 @@ const char *master = master_.UTF8String; const char *mid = strstr(master, "/"); if (!mid) mid = master; masterpath = @(mid); - masterbase = [[OFString alloc] initWithUTF8String:master - length:mid - master]; + masterbase = [OFString stringWithUTF8String:master length:mid - master]; serverdesc = sdesc; if (listen) { ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; pongsock = Index: src/weapon.mm ================================================================== --- src/weapon.mm +++ src/weapon.mm @@ -146,14 +146,12 @@ DynamicEntity *owner, int gun) { for (size_t i = 0; i < MAXPROJ; i++) { Projectile *p = projs[i]; - if (p == nil) { - p = [[Projectile alloc] init]; - projs[i] = p; - } + if (p == nil) + projs[i] = p = [Projectile projectile]; if (p.inuse) continue; p.inuse = true; @@ -352,12 +350,12 @@ vmul(v, damage / dist / 50); vadd(d.vel, v); } void -raydamage( - DynamicEntity *o, const OFVector3D &from, const OFVector3D &to, DynamicEntity *d, int i) +raydamage(DynamicEntity *o, const OFVector3D &from, const OFVector3D &to, + DynamicEntity *d, int i) { if (o.state != CS_ALIVE) return; int qdam = guns[d.gunselect].damage; if (d.quadmillis)