Cube  Check-in [f8f97851f3]

Overview
Comment:Make conoutf take an OFString
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: f8f97851f3ec3f0920a95912e7bfe58995f5ca1bcd4c5b5ee3532cb1809eab46
User & Date: js on 2024-08-03 17:02:33
Other Links: manifest | tags
Context
2024-08-03
17:11
Improve clang-format file check-in: 2bc02cf470 user: js tags: trunk
17:02
Make conoutf take an OFString check-in: f8f97851f3 user: js tags: trunk
15:12
Start migrating commands / variables to ObjC++ check-in: f4c57c1df0 user: js tags: trunk
Changes

Modified src/client.mm from [217fc4ecf8] to [65bd73d8a9].

9
10
11
12
13
14
15
16

17
18
19
20
21
22
23


24
25

26
27
28
29
30

31
32


33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60

61

62
63
64
65
66
67

68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84

85
86
87

88
89
90
91
92
93
94
95
96
97
98
99

100
101
102


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138


139
140
141
142
143
144

145
146

147
148

149
150
151
152


153
154

155
156
157
158
159
160

161

162

163
164
165
166

167

168
169
170
171
172
173
174
9
10
11
12
13
14
15

16
17
18
19
20
21
22

23
24
25

26
27
28
29
30
31
32
33

34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64

65
66
67
68
69
70

71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87

88
89
90

91
92
93
94
95
96
97
98
99
100
101
102

103
104


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140


141
142
143
144
145
146
147

148
149

150
151

152
153
154


155
156
157

158
159
160
161
162
163

164
165
166

167
168
169
170

171

172
173
174
175
176
177
178
179







-
+






-
+
+

-
+





+

-
+
+

-
+



















-
+






+
-
+





-
+








-
+







-
+


-
+











-
+

-
-
+
+



















-
+














-
-
+
+





-
+

-
+

-
+


-
-
+
+

-
+





-
+

+
-
+



-
+
-
+







int clientnum = -1;   // our client id in the game
bool c2sinit = false; // whether we need to tell the other clients our stats

int
getclientnum()
{
	return clientnum;
};
}

bool
multiplayer()
{
	// check not correct on listen server?
	if (clienthost)
		conoutf("operation not available in multiplayer");
		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)");
		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(char *name)
{
	c2sinit = false;
	strn0cpy(player1->name, name, 16);
}
};

void
newteam(char *name)
{
	c2sinit = false;
	strn0cpy(player1->team, name, 5);
};
}

COMMANDN(team, newteam, ARG_1STR);
COMMANDN(name, newname, ARG_1STR);

void
writeclientinfo(FILE *f)
{
	fprintf(f, "name \"%s\"\nteam \"%s\"\n", player1->name, player1->team);
};
}

void
connects(char *servername)
{
	disconnect(1); // reset state
	addserver(servername);

	conoutf("attempting to connect to %s", servername);
	conoutf(@"attempting to connect to %s", servername);
	ENetAddress address = {ENET_HOST_ANY, CUBE_SERVER_PORT};
	if (enet_address_set_host(&address, servername) < 0) {
		conoutf("could not resolve server %s", servername);
		conoutf(@"could not resolve server %s", 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");
		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");
		conoutf(@"disconnected");
	clienthost = NULL;
	connecting = 0;
	connattempts = 0;
	disconnecting = 0;
	clientnum = -1;
	c2sinit = false;
	player1->lifesequence = 0;
	loopv(players) zapdynent(players[i]);

	localdisconnect();

	if (!onlyclean) {
		stop();
		localconnect();
	};
};
	}
}

void
trydisconnect()
{
	if (!clienthost) {
		conoutf("not connected");
		conoutf(@"not connected");
		return;
	};
	}
	if (connecting) {
		conoutf("aborting connection attempt");
		conoutf(@"aborting connection attempt");
		disconnect();
		return;
	};
	conoutf("attempting to disconnect...");
	}
	conoutf(@"attempting to disconnect...");
	disconnect(0, !disconnecting);
};
}

string ctext;
void
toserver(char *text)
{
	conoutf("%s:\f %s", player1->name, text);
	conoutf(@"%s:\f %s", player1->name, text);
	strn0cpy(ctext, text, 80);
}
};

void
echo(char *text)
{
	conoutf("%s", text);
	conoutf(@"%s", text);
};
}

COMMAND(echo, ARG_VARI);
COMMANDN(say, toserver, ARG_VARI);
COMMANDN(connect, connects, ARG_1STR);
COMMANDN(disconnect, trydisconnect, ARG_NONE);

// collect c2s messages conveniently
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197
198
199
200

201
202
203
204
205

206
207

208
209
210
211
212
213
214
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
203
204

205
206
207
208
209

210
211

212
213
214
215
216
217
218
219







-
+










-
+




-
+

-
+







		return;
	if (num != msgsizelookup(type)) {
		sprintf_sd(s)("inconsistant msg size for %d (%d != %d)", type,
		    num, msgsizelookup(type));
		fatal(s);
	};
	if (messages.length() == 100) {
		conoutf("command flood protection (type %d)", type);
		conoutf(@"command flood protection (type %d)", type);
		return;
	};
	ivector &msg = messages.add();
	msg.add(num);
	msg.add(rel);
	msg.add(type);
	va_list marker;
	va_start(marker, type);
	loopi(num - 1) msg.add(va_arg(marker, int));
	va_end(marker);
};
}

void
server_err()
{
	conoutf("server network error, disconnecting...");
	conoutf(@"server network error, disconnecting...");
	disconnect();
};
}

int lastupdate = 0, lastping = 0;
string toservermap;
bool senditemstoserver =
    false; // after a map change, since server doesn't have map data

string clientpassword;
341
342
343
344
345
346
347
348

349
350
351
352

353
354
355
356
357
358
359
360
361

362
363
364
365
366
367
368

369
370
371
372
373
374
375
346
347
348
349
350
351
352

353
354
355
356

357
358
359
360
361
362
363
364
365

366
367
368
369
370
371
372

373
374
375
376
377
378
379
380







-
+



-
+








-
+






-
+







void
gets2c() // get updates from the server
{
	ENetEvent event;
	if (!clienthost)
		return;
	if (connecting && lastmillis / 3000 > connecting / 3000) {
		conoutf("attempting to connect...");
		conoutf(@"attempting to connect...");
		connecting = lastmillis;
		++connattempts;
		if (connattempts > 3) {
			conoutf("could not connect to server");
			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");
			conoutf(@"connected to server");
			connecting = 0;
			throttle();
			break;

		case ENET_EVENT_TYPE_RECEIVE:
			if (disconnecting)
				conoutf("attempting to disconnect...");
				conoutf(@"attempting to disconnect...");
			else
				localservertoclient(event.packet->data,
				    event.packet->dataLength);
			enet_packet_destroy(event.packet);
			break;

		case ENET_EVENT_TYPE_DISCONNECT:

Modified src/clientextras.mm from [1f60f5711b] to [3d0c884313].

175
176
177
178
179
180
181
182

183
184
185
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

211
212
213
214
175
176
177
178
179
180
181

182
183
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

210
211
212
213
214







-
+










-
+
















-
+




	    NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE);
	uchar *start = packet->data;
	uchar *p = start + 2;
	putint(p, SV_SENDMAP);
	sendstring(mapname, p);
	putint(p, mapsize);
	if (65535 - (p - start) < mapsize) {
		conoutf("map %s is too large to send", mapname);
		conoutf(@"map %s is too large to send", mapname);
		free(mapdata);
		enet_packet_destroy(packet);
		return;
	};
	memcpy(p, mapdata, mapsize);
	p += mapsize;
	free(mapdata);
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	sendpackettoserv(packet);
	conoutf("sending map %s to server...", mapname);
	conoutf(@"sending map %s to server...", mapname);
	sprintf_sd(msg)(
	    "[map %s uploaded to server, \"getmap\" to receive it]", mapname);
	toserver(msg);
}

void
getmap()
{
	ENetPacket *packet =
	    enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
	uchar *start = packet->data;
	uchar *p = start + 2;
	putint(p, SV_RECVMAP);
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	sendpackettoserv(packet);
	conoutf("requesting map from server...");
	conoutf(@"requesting map from server...");
}

COMMAND(sendmap, ARG_1STR);
COMMAND(getmap, ARG_NONE);

Modified src/clientgame.mm from [f88f2d9175] to [8334b2a352].

154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177

178
179

180
181
182
183
184
185
186
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172
173

174
175
176

177
178

179
180
181
182
183
184
185
186







-
+












-
+


-
+

-
+








void
arenarespawn()
{
	if (arenarespawnwait) {
		if (arenarespawnwait < lastmillis) {
			arenarespawnwait = 0;
			conoutf("new round starting... fight!");
			conoutf(@"new round starting... fight!");
			respawnself();
		};
	} else if (arenadetectwait == 0 || arenadetectwait < lastmillis) {
		arenadetectwait = 0;
		int alive = 0, dead = 0;
		char *lastteam = NULL;
		bool oneteam = true;
		loopv(players) if (players[i])
		    arenacount(players[i], alive, dead, lastteam, oneteam);
		arenacount(player1, alive, dead, lastteam, oneteam);
		if (dead > 0 && (alive <= 1 || (m_teammode && oneteam))) {
			conoutf(
			    "arena round is over! next round in 5 seconds...");
			    @"arena round is over! next round in 5 seconds...");
			if (alive)
				conoutf(
				    "team %s is last man standing", lastteam);
				    @"team %s is last man standing", lastteam);
			else
				conoutf("everyone died!");
				conoutf(@"everyone died!");
			arenarespawnwait = lastmillis + 5000;
			arenadetectwait = lastmillis + 10000;
			player1->roll = 0;
		};
	};
};

214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228







-
+








void
respawn()
{
	if (player1->state == CS_DEAD) {
		player1->attacking = false;
		if (m_arena) {
			conoutf("waiting for new round to start...");
			conoutf(@"waiting for new round to start...");
			return;
		};
		if (m_sp) {
			nextmode = gamemode;
			changemap(clientmap);
			return;
		}; // if we die in SP we try the same map again
295
296
297
298
299
300
301
302
303


304
305
306
307
308
309
310
295
296
297
298
299
300
301


302
303
304
305
306
307
308
309
310







-
-
+
+







		d->o.x += dx;
		d->o.y += dy;
		if (collide(d, true, 0, 0))
			return;
		d->o.x -= dx;
		d->o.y -= dy;
	};
	conoutf(
	    "can't find entity spawn spot! (%d, %d)", (int)d->o.x, (int)d->o.y);
	conoutf(@"can't find entity spawn spot! (%d, %d)", (int)d->o.x,
	    (int)d->o.y);
	// leave ent at original pos, possibly stuck
};

int spawncycle = -1;
int fixspawn = 2;

void
418
419
420
421
422
423
424
425

426
427
428

429
430
431
432
433
434
435


436
437
438
439

440
441
442



443
444
445
446
447
448
449
450
451
452
453
454
455
456


457
458
459
460
461
462
463
464
465


466
467
468

469
470


471
472
473
474
475
476
477
478
479
480
481
482

483
484
485
486
487
488
489
490
491
492
493
494
495
496

497

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515

516
517
518
418
419
420
421
422
423
424

425
426
427

428
429
430
431
432
433


434
435
436
437
438

439



440
441
442
443
444
445
446
447
448
449
450
451
452
453
454


455
456
457
458
459
460
461
462
463


464
465
466
467

468


469
470
471
472
473
474
475
476
477
478
479
480
481

482
483
484
485
486
487
488
489
490
491
492
493
494
495

496

497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514

515
516
517
518







-
+


-
+





-
-
+
+



-
+
-
-
-
+
+
+












-
-
+
+







-
-
+
+


-
+
-
-
+
+











-
+













-
+
-
+

















-
+



	        : (player1->roll < 0
	                  ? -droll
	                  : (rnd(2) ? droll
	                            : -droll)); // give player a kick depending
	                                        // on amount of damage
	if ((player1->health -= damage) <= 0) {
		if (actor == -2) {
			conoutf("you got killed by %s!", act->name);
			conoutf(@"you got killed by %s!", act->name);
		} else if (actor == -1) {
			actor = getclientnum();
			conoutf("you suicided!");
			conoutf(@"you suicided!");
			addmsg(1, 2, SV_FRAGS, --player1->frags);
		} else {
			dynent *a = getclient(actor);
			if (a) {
				if (isteam(a->team, player1->team)) {
					conoutf("you got fragged by a teammate "
					        "(%s)",
					conoutf(@"you got fragged by a "
					        @"teammate (%s)",
					    a->name);
				} else {
					conoutf(
					    "you got fragged by %s", a->name);
					    @"you got fragged by %s", a->name);
				};
			};
		};
				}
			}
		}
		showscores(true);
		addmsg(1, 2, SV_DIED, actor);
		player1->lifesequence++;
		player1->attacking = false;
		player1->state = CS_DEAD;
		player1->pitch = 0;
		player1->roll = 60;
		playsound(S_DIE1 + rnd(2));
		spawnstate(player1);
		player1->lastaction = lastmillis;
	} else {
		playsound(S_PAIN6);
	};
};
	}
}

void
timeupdate(int timeremain)
{
	if (!timeremain) {
		intermission = true;
		player1->attacking = false;
		conoutf("intermission:");
		conoutf("game has ended!");
		conoutf(@"intermission:");
		conoutf(@"game has ended!");
		showscores(true);
	} else {
		conoutf("time remaining: %d minutes", timeremain);
		conoutf(@"time remaining: %d minutes", timeremain);
	};
};
	}
}

dynent *
getclient(int cn) // ensure valid entity
{
	if (cn < 0 || cn >= MAXCLIENTS) {
		neterr("clientnum");
		return NULL;
	};
	while (cn >= players.length())
		players.add(NULL);
	return players[cn] ? players[cn] : (players[cn] = newdynent());
};
}

void
initclient()
{
	clientmap[0] = 0;
	initclientnet();
};

void
startmap(char *name) // called just after a map load
{
	if (netmapstart() && m_sp) {
		gamemode = 0;
		conoutf("coop sp not supported yet");
		conoutf(@"coop sp not supported yet");
	};
	}
	sleepwait = 0;
	monsterclear();
	projreset();
	spawncycle = -1;
	spawnplayer(player1);
	player1->frags = 0;
	loopv(players) if (players[i]) players[i]->frags = 0;
	resetspawns();
	strcpy_s(clientmap, name);
	if (editmode)
		toggleedit();
	setvar("gamespeed", 100);
	setvar("fog", 180);
	setvar("fogcolour", 0x8099B3);
	showscores(false);
	intermission = false;
	framesinmap = 0;
	conoutf("game mode is %s", modestr(gamemode));
	conoutf(@"game mode is %s", modestr(gamemode));
};

COMMANDN(map, changemap, ARG_1STR);

Modified src/clients2c.mm from [eb9667cf69] to [e92e9ee6ab].

1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20












-
+







// client processing of the incoming network stream

#include "cube.h"

extern int clientnum;
extern bool c2sinit, senditemstoserver;
extern string toservermap;
extern string clientpassword;

void
neterr(char *s)
{
	conoutf("illegal network message (%s)", s);
	conoutf(@"illegal network message (%s)", s);
	disconnect();
};

void
changemapserv(char *name, int mode) // forced map change from the server
{
	gamemode = mode;
72
73
74
75
76
77
78
79
80


81
82
83
84
85
86
87
88
89
90
91
92
93
94


95
96
97
98
99

100
101
102
103
104
105
106
72
73
74
75
76
77
78


79
80
81
82
83
84
85
86
87
88
89
90
91
92


93
94
95
96
97
98

99
100
101
102
103
104
105
106







-
-
+
+












-
-
+
+




-
+







	while (p < end)
		switch (type = getint(p)) {
		case SV_INITS2C: // welcome messsage from the server
		{
			cn = getint(p);
			int prot = getint(p);
			if (prot != PROTOCOL_VERSION) {
				conoutf("you are using a different game "
				        "protocol (you: %d, server: %d)",
				conoutf(@"you are using a different game "
				        @"protocol (you: %d, server: %d)",
				    PROTOCOL_VERSION, prot);
				disconnect();
				return;
			};
			toservermap[0] = 0;
			clientnum = cn; // we are now fully connected
			if (!getint(p))
				strcpy_s(toservermap,
				    getclientmap()); // we are the first client
				                     // on this server, set map
			sgetstr();
			if (text[0] && strcmp(text, clientpassword)) {
				conoutf("you need to set the correct password "
				        "to join this server!");
				conoutf(@"you need to set the correct password "
				        @"to join this server!");
				disconnect();
				return;
			};
			if (getint(p) == 1) {
				conoutf("server is FULL, disconnecting..");
				conoutf(@"server is FULL, disconnecting..");
			};
			break;
		};

		case SV_POS: // position of another client
		{
			cn = getint(p);
132
133
134
135
136
137
138
139

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
176
177

178
179
180
181
182
183

184
185
186
187
188
189
190

191
192
193
194
195
196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214

215
216
217
218
219
220
221
222
223
224
225
226
227

228
229
230
231
232

233
234
235
236
237

238
239
240
241

242

243
244
245
246
247
248
249


250
251
252

253
254
255
256



257
258
259
260

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

281
282
283
284
285
286
287
132
133
134
135
136
137
138

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176

177
178
179
180
181
182

183
184
185
186
187
188
189

190
191
192
193
194
195

196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221
222
223
224
225
226

227
228
229
230
231

232
233
234
235
236

237
238
239
240

241

242
243
244
245
246
247


248
249
250
251

252
253



254
255
256
257
258
259

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
287







-
+


















-
+









-
+








-
+





-
+






-
+





-
+

















-
+












-
+




-
+




-
+



-
+
-
+





-
-
+
+


-
+

-
-
-
+
+
+



-
+



















-
+








		case SV_SOUND:
			playsound(getint(p), &d->o);
			break;

		case SV_TEXT:
			sgetstr();
			conoutf("%s:\f %s", d->name, text);
			conoutf(@"%s:\f %s", d->name, text);
			break;

		case SV_MAPCHANGE:
			sgetstr();
			changemapserv(text, getint(p));
			mapchanged = true;
			break;

		case SV_ITEMLIST: {
			int n;
			if (mapchanged) {
				senditemstoserver = false;
				resetspawns();
			};
			while ((n = getint(p)) != -1)
				if (mapchanged)
					setspawn(n, true);
			break;
		};
		}

		case SV_MAPRELOAD: // server requests next map
		{
			getint(p);
			sprintf_sd(nextmapalias)("nextmap_%s", getclientmap());
			char *map =
			    getalias(nextmapalias); // look up map in the cycle
			changemap(map ? map : getclientmap());
			break;
		};
		}

		case SV_INITC2S: // another client either connected or changed
		                 // name/team
		{
			sgetstr();
			if (d->name[0]) // already connected
			{
				if (strcmp(d->name, text))
					conoutf("%s is now known as %s",
					conoutf(@"%s is now known as %s",
					    d->name, text);
			} else // new client
			{
				c2sinit =
				    false; // send new players my info again
				conoutf("connected: %s", text);
				conoutf(@"connected: %s", text);
			};
			strcpy_s(d->name, text);
			sgetstr();
			strcpy_s(d->team, text);
			d->lifesequence = getint(p);
			break;
		};
		}

		case SV_CDIS:
			cn = getint(p);
			if (!(d = getclient(cn)))
				break;
			conoutf("player %s disconnected",
			conoutf(@"player %s disconnected",
			    d->name[0] ? d->name : "[incompatible client]");
			zapdynent(players[cn]);
			break;

		case SV_SHOT: {
			int gun = getint(p);
			vec s, e;
			s.x = getint(p) / DMF;
			s.y = getint(p) / DMF;
			s.z = getint(p) / DMF;
			e.x = getint(p) / DMF;
			e.y = getint(p) / DMF;
			e.z = getint(p) / DMF;
			if (gun == GUN_SG)
				createrays(s, e);
			shootv(gun, s, e, d);
			break;
		};
		}

		case SV_DAMAGE: {
			int target = getint(p);
			int damage = getint(p);
			int ls = getint(p);
			if (target == clientnum) {
				if (ls == player1->lifesequence)
					selfdamage(damage, cn, d);
			} else
				playsound(
				    S_PAIN1 + rnd(5), &getclient(target)->o);
			break;
		};
		}

		case SV_DIED: {
			int actor = getint(p);
			if (actor == cn) {
				conoutf("%s suicided", d->name);
				conoutf(@"%s suicided", d->name);
			} else if (actor == clientnum) {
				int frags;
				if (isteam(player1->team, d->team)) {
					frags = -1;
					conoutf("you fragged a teammate (%s)",
					conoutf(@"you fragged a teammate (%s)",
					    d->name);
				} else {
					frags = 1;
					conoutf("you fragged %s", d->name);
					conoutf(@"you fragged %s", d->name);
				};
				}
				addmsg(1, 2, SV_FRAGS, player1->frags += frags);
			} else {
				dynent *a = getclient(actor);
				if (a) {
					if (isteam(a->team, d->name)) {
						conoutf("%s fragged his "
						        "teammate (%s)",
						conoutf(@"%s fragged his "
						        @"teammate (%s)",
						    a->name, d->name);
					} else {
						conoutf("%s fragged %s",
						conoutf(@"%s fragged %s",
						    a->name, d->name);
					};
				};
			};
					}
				}
			}
			playsound(S_DIE1 + rnd(2), &d->o);
			d->lifesequence++;
			break;
		};
		}

		case SV_FRAGS:
			players[cn]->frags = getint(p);
			break;

		case SV_ITEMPICKUP:
			setspawn(getint(p), false);
			getint(p);
			break;

		case SV_ITEMSPAWN: {
			uint i = getint(p);
			setspawn(i, true);
			if (i >= (uint)ents.length())
				break;
			vec v = {(float)ents[i].x, (float)ents[i].y,
			    (float)ents[i].z};
			playsound(S_ITEMSPAWN, &v);
			break;
		};
		}

		case SV_ITEMACC: // server acknowledges that I picked up this
		                 // item
			realpickup(getint(p), player1);
			break;

		case SV_EDITH: // coop editing messages, should be extended to
308
309
310
311
312
313
314
315

316
317

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

338
339
340
341
342
343
344
308
309
310
311
312
313
314

315
316

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336

337
338
339
340
341
342
343
344







-
+

-
+



















-
+







				break;
			case SV_EDITD:
				setvdeltaxy(v, b);
				break;
			case SV_EDITE:
				editequalisexy(v != 0, b);
				break;
			};
			}
			break;
		};
		}

		case SV_EDITENT: // coop edit of ent
		{
			uint i = getint(p);
			while ((uint)ents.length() <= i)
				ents.add().type = NOTUSED;
			int to = ents[i].type;
			ents[i].type = getint(p);
			ents[i].x = getint(p);
			ents[i].y = getint(p);
			ents[i].z = getint(p);
			ents[i].attr1 = getint(p);
			ents[i].attr2 = getint(p);
			ents[i].attr3 = getint(p);
			ents[i].attr4 = getint(p);
			ents[i].spawned = false;
			if (ents[i].type == LIGHT || to == LIGHT)
				calclight();
			break;
		};
		}

		case SV_PING:
			getint(p);
			break;

		case SV_PONG:
			addmsg(0, 2, SV_CLIENTPING,
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371

372
373
374
375

376
377
378
379
380
381
382
383
384

385
386
387
388
389
390


357
358
359
360
361
362
363

364
365
366
367
368
369
370

371
372
373
374

375
376
377
378
379
380
381
382
383

384
385
386
387
388


389
390







-
+






-
+



-
+








-
+




-
-
+
+

		case SV_TIMEUP:
			timeupdate(getint(p));
			break;

		case SV_RECVMAP: {
			sgetstr();
			conoutf("received map \"%s\" from server, reloading..",
			conoutf(@"received map \"%s\" from server, reloading..",
			    text);
			int mapsize = getint(p);
			writemap(text, mapsize, p);
			p += mapsize;
			changemapserv(text, gamemode);
			break;
		};
		}

		case SV_SERVMSG:
			sgetstr();
			conoutf("%s", text);
			conoutf(@"%s", text);
			break;

		case SV_EXT: // so we can messages without breaking previous
		             // clients/servers, if necessary
		{
			for (int n = getint(p); n; n--)
				getint(p);
			break;
		};
		}

		default:
			neterr("type");
			return;
		};
};
		}
}

Modified src/command.mm from [ccec6a17e5] to [61c1fa172a].

50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64







-
+








			idents[@(name)] = b;
		} else {
			if (b.type == ID_ALIAS)
				b.action = exchangestr(b.action, action);
			else
				conoutf(
				    "cannot redefine builtin %s with an alias",
				    @"cannot redefine builtin %s with an alias",
				    name);
		}
	}
}

COMMAND(alias, ARG_2STR)

148
149
150
151
152
153
154
155

156
157
158
159
160
161
162
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162







-
+







			*(p - 1) = ' '; // hack
		if (c == left)
			brak++;
		else if (c == right)
			brak--;
		else if (!c) {
			p--;
			conoutf("missing \"%c\"", right);
			conoutf(@"missing \"%c\"", right);
			return NULL;
		}
	}
	char *s = newstring(word, p - word - 1);
	if (left == '(') {
		string t;
		itoa(t,
207
208
209
210
211
212
213
214

215
216
217
218
219
220
221
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221







-
+







				return exchangestr(n, t);
			case ID_ALIAS:
				return exchangestr(n, ID.action);
			}
		}
	}

	conoutf("unknown alias lookup: %s", n + 1);
	conoutf(@"unknown alias lookup: %s", n + 1);
	return n;
}

int
execute(char *p, bool isdown) // all evaluation happens here, recursively
{
	const int MAXWORDS = 25; // limit, remove
250
251
252
253
254
255
256
257

258
259
260
261
262
263
264
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264







-
+








		@autoreleasepool {
			Ident *ID = idents[@(c)];

			if (ID == nil) {
				val = ATOI(c);
				if (!val && *c != '0')
					conoutf("unknown command: %s", c);
					conoutf(@"unknown command: %s", c);
			} else {
				switch (ID.type) {
				// game defined commands
				case ID_COMMAND:
					// use very ad-hoc function signature,
					// and just call it
					switch (ID.narg) {
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405





406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436




























437
438
439
440
441
442
443
391
392
393
394
395
396
397

398
399
400
401




402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422















423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457







-
+



-
-
-
-
+
+
+
+
+
















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







				// game defined variables
				case ID_VAR:
					if (isdown) {
						if (!w[1][0])
							// var with no value
							// just prints its
							// current value
							conoutf("%s = %d", c,
							conoutf(@"%s = %d", c,
							    *ID.storage);
						else {
							if (ID.min > ID.max) {
								conoutf("variab"
								        "le is "
								        "read-"
								        "only");
								conoutf(
								    @"variable "
								    @"is "
								    @"read-"
								    @"only");
							} else {
								int i1 =
								    ATOI(w[1]);
								if (i1 <
								        ID.min ||
								    i1 >
								        ID.max) {
									// clamp
									// to
									// valid
									// range
									i1 =
									    i1 < ID.min
									        ? ID.min
									        : ID.max;
									conoutf(
									    "va"
									    "li"
									    "d "
									    "ra"
									    "ng"
									    "e "
									    "fo"
									    "r "
									    "%s"
									    " i"
									    "s "
									    "%d"
									    ".."
									    "%"
									    "d",
									    @"v"
									    @"a"
									    @"l"
									    @"i"
									    @"d"
									    @" "
									    @"r"
									    @"a"
									    @"n"
									    @"g"
									    @"e"
									    @" "
									    @"f"
									    @"o"
									    @"r"
									    @" "
									    @"%"
									    @"s"
									    @" "
									    @"i"
									    @"s"
									    @" "
									    @"%"
									    @"d"
									    @"."
									    @"."
									    @"%"
									    @"d",
									    c,
									    ID.min,
									    ID.max);
								}
								*ID.storage =
								    i1;
							}
529
530
531
532
533
534
535
536

537
538
539
540
541
542
543
543
544
545
546
547
548
549

550
551
552
553
554
555
556
557







-
+







	return true;
}

void
exec(char *cfgfile)
{
	if (!execfile(cfgfile))
		conoutf("could not read \"%s\"", cfgfile);
		conoutf(@"could not read \"%s\"", cfgfile);
}

void
writecfg()
{
	FILE *f = fopen("config.cfg", "w");
	if (!f)

Modified src/console.mm from [ee8378b098] to [1df29be187].

46
47
48
49
50
51
52
53

54
55
56


57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
46
47
48
49
50
51
52

53
54


55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71







-
+

-
-
+
+







-
+







	puts(cl.cref);
#ifndef _WIN32
	fflush(stdout);
#endif
};

void
conoutf(const char *s, ...)
conoutf(OFString *str, ...)
{
	sprintf_sdv(sf, s);
	s = sf;
	sprintf_sdv(sf, str.UTF8String);
	const char *s = sf;
	int n = 0;
	while (strlen(s) > WORDWRAP) // cut strings to fit on screen
	{
		string t;
		strn0cpy(t, s, WORDWRAP + 1);
		conline(t, n++ != 0);
		s += WORDWRAP;
	};
	}
	conline(s, n != 0);
};

void
renderconsole() // render buffer taking into account time & scrolling
{
	int nd = 0;
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
110
111
112
113
114
115
116

117
118
119
120
121
122
123
124







-
+







	for (char *x = key; *x; x++)
		*x = toupper(*x);
	loopi(numkm) if (strcmp(keyms[i].name, key) == 0)
	{
		strcpy_s(keyms[i].action, action);
		return;
	};
	conoutf("unknown key \"%s\"", key);
	conoutf(@"unknown key \"%s\"", key);
};

COMMANDN(bind, bindkey, ARG_2STR);

void
saycommand(char *init) // turns input to the command line on or off
{

Modified src/cube.h from [ac53062292] to [9271127084].

390
391
392
393
394
395
396
397

398
399
400
401
402
403
404
405
406

407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422
423

424
425
426
427
428
429
430
431
432
433
434

435
436
437
438
439
440
441
390
391
392
393
394
395
396

397
398
399
400
401
402
403
404
405

406
407
408
409
410
411
412
413

414
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429
430
431
432
433

434
435
436
437
438
439
440
441







-
+








-
+







-
+








-
+










-
+







};

// nasty macros for registering script functions, abuses globals to avoid
// excessive infrastructure
#define COMMANDN(name, fun, nargs)                                             \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		enqueueInit(#name, ^{                                          \
		  addcommand(#name, (void (*)())fun, nargs);                   \
		});                                                            \
	}
#define COMMAND(name, nargs) COMMANDN(name, name, nargs)
#define VARP(name, min, cur, max)                                              \
	int name;                                                              \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		enqueueInit(#name, ^{                                          \
		  name = variable(#name, min, cur, max, &name, NULL, true);    \
		});                                                            \
	}
#define VAR(name, min, cur, max)                                               \
	int name;                                                              \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		enqueueInit(#name, ^{                                          \
		  name = variable(#name, min, cur, max, &name, NULL, false);   \
		});                                                            \
	}
#define VARF(name, min, cur, max, body)                                        \
	void var_##name();                                                     \
	static int name;                                                       \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		enqueueInit(#name, ^{                                          \
		  name = variable(                                             \
		      #name, min, cur, max, &name, var_##name, false);         \
		});                                                            \
	}                                                                      \
	void var_##name() { body; }
#define VARFP(name, min, cur, max, body)                                       \
	void var_##name();                                                     \
	static int name;                                                       \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		enqueueInit(#name, ^{                                          \
		  name =                                                       \
		      variable(#name, min, cur, max, &name, var_##name, true); \
		});                                                            \
	}                                                                      \
	void var_##name() { body; }

#define ATOI(s) strtol(s, NULL, 0) // supports hexadecimal numbers

Modified src/editing.mm from [df7a7d4fb4] to [35e47e0fb8].

83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98

99
100
101
102
103
104
105
83
84
85
86
87
88
89

90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105







-
+







-
+







};

bool
noteditmode()
{
	correctsel();
	if (!editmode)
		conoutf("this function is only allowed in edit mode");
		conoutf(@"this function is only allowed in edit mode");
	return !editmode;
};

bool
noselection()
{
	if (!selset)
		conoutf("no selection");
		conoutf(@"no selection");
	return !selset;
};

#define EDITSEL                                                                \
	if (noteditmode() || noselection())                                    \
		return;
#define EDITSELMP                                                              \
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268







-
+







};

void
editundo()
{
	EDITMP;
	if (undos.empty()) {
		conoutf("nothing more to undo");
		conoutf(@"nothing more to undo");
		return;
	};
	block *p = undos.pop();
	blockpaste(*p);
	free(p);
};

278
279
280
281
282
283
284
285

286
287
288
289
290
291
292

293
294
295
296
297
298
299
278
279
280
281
282
283
284

285
286
287
288
289
290
291

292
293
294
295
296
297
298
299







-
+






-
+







};

void
paste()
{
	EDITMP;
	if (!copybuf) {
		conoutf("nothing to paste");
		conoutf(@"nothing to paste");
		return;
	};
	sel.xs = copybuf->xs;
	sel.ys = copybuf->ys;
	correctsel();
	if (!selset || sel.xs != copybuf->xs || sel.ys != copybuf->ys) {
		conoutf("incorrect selection");
		conoutf(@"incorrect selection");
		return;
	};
	makeundo();
	copybuf->x = sel.x;
	copybuf->y = sel.y;
	blockpaste(*copybuf);
};
436
437
438
439
440
441
442
443

444
445
446
447
448
449
450
436
437
438
439
440
441
442

443
444
445
446
447
448
449
450







-
+







void
edittype(int type)
{
	EDITSEL;
	if (type == CORNER &&
	    (sel.xs != sel.ys || sel.xs == 3 || sel.xs > 4 && sel.xs != 8 ||
	        sel.x & ~-sel.xs || sel.y & ~-sel.ys)) {
		conoutf("corner selection must be power of 2 aligned");
		conoutf(@"corner selection must be power of 2 aligned");
		return;
	};
	edittypexy(type, sel);
	addmsg(1, 6, SV_EDITS, sel.x, sel.y, sel.xs, sel.ys, type);
};

void

Modified src/entities.mm from [a95a1806cd] to [2a578e1163].

180
181
182
183
184
185
186
187

188
189
190


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205


206
207
208
209
210
211
212
213
214

215
216
217
218
219
220
221
180
181
182
183
184
185
186

187
188


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203


204
205
206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221







-
+

-
-
+
+













-
-
+
+








-
+







	case I_YELLOWARMOUR:
		radditem(n, d->armour);
		d->armourtype = A_YELLOW;
		break;

	case I_QUAD:
		radditem(n, d->quadmillis);
		conoutf("you got the quad!");
		conoutf(@"you got the quad!");
		break;
	};
};
	}
}

// these functions are called when the client touches the item

void
additem(int i, int &v, int spawnsec)
{
	if (v < itemstats[ents[i].type - I_SHELLS]
	            .max) // don't pick up if not needed
	{
		addmsg(1, 3, SV_ITEMPICKUP, i,
		    m_classicsp ? 100000
		                : spawnsec); // first ask the server for an ack
		ents[i].spawned = false; // even if someone else gets it first
	};
};
	}
}

void
teleport(int n, dynent *d) // also used by monsters
{
	int e = -1, tag = ents[n].attr1, beenhere = -1;
	for (;;) {
		e = findentity(TELEDEST, e + 1);
		if (e == beenhere || e < 0) {
			conoutf("no teleport destination for tag %d", tag);
			conoutf(@"no teleport destination for tag %d", tag);
			return;
		};
		if (beenhere < 0)
			beenhere = e;
		if (ents[e].attr2 == tag) {
			d->o.x = ents[e].x;
			d->o.y = ents[e].y;
328
329
330
331
332
333
334
335

336
337


338
339
340
341
342
343
344
345
346
347
348


349
350
351
352
353
354
355
356
357
358
359
360
328
329
330
331
332
333
334

335


336
337
338
339
340
341
342
343
344
345
346


347
348
349
350
351
352
353
354
355
356
357
358
359
360







-
+
-
-
+
+









-
-
+
+













void
checkquad(int time)
{
	if (player1->quadmillis && (player1->quadmillis -= time) < 0) {
		player1->quadmillis = 0;
		playsoundc(S_PUPOUT);
		conoutf("quad damage is over");
		conoutf(@"quad damage is over");
	};
};
	}
}

void
putitems(uchar *&p) // puts items in network stream and also spawns them locally
{
	loopv(ents) if ((ents[i].type >= I_SHELLS && ents[i].type <= I_QUAD) ||
	                ents[i].type == CARROT)
	{
		putint(p, i);
		ents[i].spawned = true;
	};
};
	}
}

void
resetspawns()
{
	loopv(ents) ents[i].spawned = false;
};
void
setspawn(uint i, bool on)
{
	if (i < (uint)ents.length())
		ents[i].spawned = on;
};

Modified src/main.mm from [65508559f3] to [6994beff69].

102
103
104
105
106
107
108
109

110
111
112
113
114
115
116
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116







-
+







	bool dedicated = false;
	int fs = SDL_FULLSCREEN, par = 0, uprate = 0, maxcl = 4;
	char *sdesc = "", *ip = "", *master = NULL, *passwd = "";
	islittleendian = *((char *)&islittleendian);

	processInitQueue();

#define log(s) conoutf("init: %s", s)
#define log(s) conoutf(@"init: %s", s)
	log("sdl");

	for (int i = 1; i < argc; i++) {
		char *a = &argv[i][2];
		if (argv[i][0] == '-')
			switch (argv[i][1]) {
			case 'd':
140
141
142
143
144
145
146
147

148
149
150

151
152
153
154
155
156
157
140
141
142
143
144
145
146

147
148
149

150
151
152
153
154
155
156
157







-
+


-
+







			case 'p':
				passwd = a;
				break;
			case 'c':
				maxcl = atoi(a);
				break;
			default:
				conoutf("unknown commandline option");
				conoutf(@"unknown commandline option");
			}
		else
			conoutf("unknown commandline argument");
			conoutf(@"unknown commandline argument");
	};

#ifdef _DEBUG
	par = SDL_INIT_NOPARACHUTE;
	fs = 0;
#endif

Modified src/monster.mm from [c901f05b50] to [1a6c1928af].

1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
1
2
3
4
5
6
7

8
9
10
11
12
13
14
15







-
+







// monster.cpp: implements AI for single player monsters, currently client only

#include "cube.h"

dvector monsters;
int nextmonster, spawnremain, numkilled, monstertotal, mtimestart;

VARF(skill, 1, 3, 10, conoutf("skill is now %d", skill));
VARF(skill, 1, 3, 10, conoutf(@"skill is now %d", skill));

dvector &
getmonsters()
{
	return monsters;
};
void
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62







-
+







        "a goblin", "monster/goblin"},
};

dynent *
basicmonster(int type, int yaw, int state, int trigger, int move)
{
	if (type >= NUMMONSTERTYPES) {
		conoutf("warning: unknown monster in spawn: %d", type);
		conoutf(@"warning: unknown monster in spawn: %d", type);
		type = 0;
	};
	dynent *m = newdynent();
	monstertype *t = &monstertypes[m->mtype = type];
	m->eyeheight = 2.0f;
	m->aboveeye = 1.9f;
	m->radius *= t->bscale / 10.0f;
336
337
338
339
340
341
342
343

344
345
346
347
348
349
350
351
352
353
354



355
356
357
358
359
360
361
362
363
364
365

366
367
368
369
370
371
372
336
337
338
339
340
341
342

343
344
345
346
347
348
349
350
351



352
353
354
355
356
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
372







-
+








-
-
-
+
+
+










-
+







		m->state = CS_DEAD;
		m->lastaction = lastmillis;
		numkilled++;
		player1->frags = numkilled;
		playsound(monstertypes[m->mtype].diesound, &m->o);
		int remain = monstertotal - numkilled;
		if (remain > 0 && remain <= 5)
			conoutf("only %d monster(s) remaining", remain);
			conoutf(@"only %d monster(s) remaining", remain);
	} else {
		playsound(monstertypes[m->mtype].painsound, &m->o);
	};
};

void
endsp(bool allkilled)
{
	conoutf(
	    allkilled ? "you have cleared the map!" : "you reached the exit!");
	conoutf("score: %d kills in %d seconds", numkilled,
	conoutf(allkilled ? @"you have cleared the map!"
	                  : @"you reached the exit!");
	conoutf(@"score: %d kills in %d seconds", numkilled,
	    (lastmillis - mtimestart) / 1000);
	monstertotal = 0;
	startintermission();
};

void
monsterthink()
{
	if (m_dmsp && spawnremain && lastmillis > nextmonster) {
		if (spawnremain-- == monstertotal)
			conoutf("The invasion has begun!");
			conoutf(@"The invasion has begun!");
		nextmonster = lastmillis + 1000;
		spawnmonster();
	};

	if (monstertotal && !spawnremain && numkilled == monstertotal)
		endsp(true);

Modified src/protos.h from [135758e2d7] to [93475710ee].

15
16
17
18
19
20
21
22

23
24
25
26
27
28
29
15
16
17
18
19
20
21

22
23
24
25
26
27
28
29







-
+







extern void alias(char *name, char *action);
extern char *getalias(char *name);
extern void writecfg();

// console
extern void keypress(int code, bool isdown, int cooked);
extern void renderconsole();
extern void conoutf(const char *s, ...);
extern void conoutf(OFString *s, ...);
extern char *getcurcommand();
extern void writebinds(FILE *f);

// init
extern void enqueueInit(const char *name, void (^init)(void));
extern void processInitQueue(void);

Modified src/rendercubes.mm from [53768c91a1] to [93318b77b9].

67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81







-
+







{
	showm = !showm;
};
void
mipstats(int a, int b, int c)
{
	if (showm)
		conoutf("1x1/2x2/4x4: %d / %d / %d", a, b, c);
		conoutf(@"1x1/2x2/4x4: %d / %d / %d", a, b, c);
};

COMMAND(showmip, ARG_NONE);

#define stripend()                                                             \
	{                                                                      \
		if (floorstrip || deltastrip) {                                \

Modified src/renderextras.mm from [6c33874cb2] to [93b4bffbbf].

206
207
208
209
210
211
212
213

214
215
216
217
218
219
220
206
207
208
209
210
211
212

213
214
215
216
217
218
219
220







-
+







	char *side[] = {"ft", "bk", "lf", "rt", "dn", "up"};
	int texnum = 14;
	loopi(6)
	{
		sprintf_sd(name)("packages/%s_%s.jpg", basename, side[i]);
		int xs, ys;
		if (!installtex(texnum + i, path(name), xs, ys, true))
			conoutf("could not load sky textures");
			conoutf(@"could not load sky textures");
	};
	strcpy_s(lastsky, basename);
};

COMMAND(loadsky, ARG_1STR);

float cursordepth = 0.9f;

Modified src/rendergl.mm from [b149b97fcf] to [155f47c457].

44
45
46
47
48
49
50
51
52


53
54
55
56
57
58
59
44
45
46
47
48
49
50


51
52
53
54
55
56
57
58
59







-
-
+
+







	glEnable(GL_CULL_FACE);

	char *exts = (char *)glGetString(GL_EXTENSIONS);

	if (strstr(exts, "GL_EXT_texture_env_combine"))
		hasoverbright = true;
	else
		conoutf("WARNING: cannot use overbright lighting, using old "
		        "lighting model!");
		conoutf(@"WARNING: cannot use overbright lighting, using old "
		        @"lighting model!");

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize);

	purgetextures();

	if (!(qsphere = gluNewQuadric()))
		fatal("glu sphere");
73
74
75
76
77
78
79
80

81
82
83
84

85
86
87
88
89
90
91
73
74
75
76
77
78
79

80
81
82
83

84
85
86
87
88
89
90
91







-
+



-
+







};

bool
installtex(int tnum, char *texname, int &xs, int &ys, bool clamp)
{
	SDL_Surface *s = IMG_Load(texname);
	if (!s) {
		conoutf("couldn't load texture %s", texname);
		conoutf(@"couldn't load texture %s", texname);
		return false;
	};
	if (s->format->BitsPerPixel != 24) {
		conoutf("texture must be 24bpp: %s", texname);
		conoutf(@"texture must be 24bpp: %s", texname);
		return false;
	};
	// loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p = 255-*p; };
	glBindTexture(GL_TEXTURE_2D, tnum);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
	    clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
99
100
101
102
103
104
105
106

107
108
109
110
111
112
113
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113







-
+







	ys = s->h;
	while (xs > glmaxtexsize || ys > glmaxtexsize) {
		xs /= 2;
		ys /= 2;
	};
	void *scaledimg = s->pixels;
	if (xs != s->w) {
		conoutf("warning: quality loss: scaling %s",
		conoutf(@"warning: quality loss: scaling %s",
		    texname); // for voodoo cards under linux
		scaledimg = alloc(xs * ys * 3);
		gluScaleImage(GL_RGB, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels,
		    xs, ys, GL_UNSIGNED_BYTE, scaledimg);
	};
	if (gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, xs, ys, GL_RGB,
	        GL_UNSIGNED_BYTE, scaledimg))
270
271
272
273
274
275
276
277
278


279
280
281
282
283
284
285
270
271
272
273
274
275
276


277
278
279
280
281
282
283
284
285







-
-
+
+







	s.num = n;
};

VARFP(gamma, 30, 100, 300, {
	float f = gamma / 100.0f;
	if (SDL_SetGamma(f, f, f) == -1) {
		conoutf(
		    "Could not set gamma (card/driver doesn't support it?)");
		conoutf("sdl: %s", SDL_GetError());
		    @"Could not set gamma (card/driver doesn't support it?)");
		conoutf(@"sdl: %s", SDL_GetError());
	};
});

void
transplayer()
{
	glLoadIdentity();

Modified src/savegamedemo.mm from [c33c84c43c] to [5f329655d6].

80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108


109
110
111
112
113
114

115
116

117
118
119
120

121

122
123
124
125
126
127
128
129
130
131

132
133

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152


153
154

155
156
157
158
159
160
161

162
163
164
165
166
167
168


169
170
171
172
173
174
175
80
81
82
83
84
85
86

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106


107
108
109
110
111
112
113

114
115

116
117
118
119

120

121
122
123
124
125
126
127
128
129
130

131
132

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150


151
152
153

154
155
156
157
158
159
160

161
162
163
164
165
166


167
168
169
170
171
172
173
174
175







-
+



















-
-
+
+





-
+

-
+



-
+
-
+









-
+

-
+

















-
-
+
+

-
+






-
+





-
-
+
+








void
savestate(char *fn)
{
	stop();
	f = gzopen(fn, "wb9");
	if (!f) {
		conoutf("could not write %s", fn);
		conoutf(@"could not write %s", fn);
		return;
	};
	gzwrite(f, (void *)"CUBESAVE", 8);
	gzputc(f, islittleendian);
	gzputi(SAVEGAMEVERSION);
	gzputi(sizeof(dynent));
	gzwrite(f, getclientmap(), _MAXDEFSTR);
	gzputi(gamemode);
	gzputi(ents.length());
	loopv(ents) gzputc(f, ents[i].spawned);
	gzwrite(f, player1, sizeof(dynent));
	dvector &monsters = getmonsters();
	gzputi(monsters.length());
	loopv(monsters) gzwrite(f, monsters[i], sizeof(dynent));
	gzputi(players.length());
	loopv(players)
	{
		gzput(players[i] == NULL);
		gzwrite(f, players[i], sizeof(dynent));
	};
};
	}
}

void
savegame(char *name)
{
	if (!m_classicsp) {
		conoutf("can only save classic sp games");
		conoutf(@"can only save classic sp games");
		return;
	};
	}
	sprintf_sd(fn)("savegames/%s.csgz", name);
	savestate(fn);
	stop();
	conoutf("wrote %s", fn);
	conoutf(@"wrote %s", fn);
};
}

void
loadstate(char *fn)
{
	stop();
	if (multiplayer())
		return;
	f = gzopen(fn, "rb9");
	if (!f) {
		conoutf("could not open %s", fn);
		conoutf(@"could not open %s", fn);
		return;
	};
	}

	string buf;
	gzread(f, buf, 8);
	if (strncmp(buf, "CUBESAVE", 8))
		goto out;
	if (gzgetc(f) != islittleendian)
		goto out; // not supporting save->load accross incompatible
		          // architectures simpifies things a LOT
	if (gzgeti() != SAVEGAMEVERSION || gzgeti() != sizeof(dynent))
		goto out;
	string mapname;
	gzread(f, mapname, _MAXDEFSTR);
	nextmode = gzgeti();
	changemap(mapname); // continue below once map has been loaded and
	                    // client & server have updated
	return;
out:
	conoutf("aborting: savegame/demo from a different version of cube or "
	        "cpu architecture");
	conoutf(@"aborting: savegame/demo from a different version of cube or "
	        @"cpu architecture");
	stop();
};
}

void
loadgame(char *name)
{
	sprintf_sd(fn)("savegames/%s.csgz", name);
	loadstate(fn);
};
}

void
loadgameout()
{
	stop();
	conoutf("loadgame incomplete: savegame from a different version of "
	        "this map");
	conoutf(@"loadgame incomplete: savegame from a different version of "
	        @"this map");
};

void
loadgamerest()
{
	if (demoplayback || !f)
		return;
208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
242

243
244
245
246
247
248
249
208
209
210
211
212
213
214

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241

242
243
244
245
246
247
248
249







-
+

















-
+








-
+







	loopi(nplayers) if (!gzget())
	{
		dynent *d = getclient(i);
		assert(d);
		gzread(f, d, sizeof(dynent));
	};

	conoutf("savegame restored");
	conoutf(@"savegame restored");
	if (demoloading)
		startdemo();
	else
		stop();
};

// demo functions

int starttime = 0;
int playbacktime = 0;
int ddamage, bdamage;
vec dorig;

void
record(char *name)
{
	if (m_sp) {
		conoutf("cannot record singleplayer games");
		conoutf(@"cannot record singleplayer games");
		return;
	};
	int cn = getclientnum();
	if (cn < 0)
		return;
	sprintf_sd(fn)("demos/%s.cdgz", name);
	savestate(fn);
	gzputi(cn);
	conoutf("started recording demo to %s", fn);
	conoutf(@"started recording demo to %s", fn);
	demorecording = true;
	starttime = lastmillis;
	ddamage = bdamage = 0;
};

void
demodamage(int damage, vec &o)
278
279
280
281
282
283
284
285

286
287
288
289


290
291
292
293
294
295
296
297

298
299
300
301
302

303
304
305
306

307
308
309
310
311
312
313

314
315
316
317
318
319
320
321

322
323

324
325
326
327
328
329
330
331

332
333
334
335
336

337
338
339
340
341
342
343
278
279
280
281
282
283
284

285
286
287


288
289
290
291
292
293
294
295
296

297
298
299
300
301

302
303
304
305

306
307
308
309
310
311
312

313
314
315
316
317
318
319
320

321
322

323
324
325
326
327
328
329
330

331
332
333
334
335

336
337
338
339
340
341
342
343







-
+


-
-
+
+







-
+




-
+



-
+






-
+







-
+

-
+







-
+




-
+







		gzput(player1->state);
		gzputi(bdamage);
		bdamage = 0;
		gzputi(ddamage);
		if (ddamage) {
			gzputv(dorig);
			ddamage = 0;
		};
		}
		// FIXME: add all other client state which is not send through
		// the network
	};
};
	}
}

void
demo(char *name)
{
	sprintf_sd(fn)("demos/%s.cdgz", name);
	loadstate(fn);
	demoloading = true;
};
}

void
stopreset()
{
	conoutf("demo stopped (%d msec elapsed)", lastmillis - starttime);
	conoutf(@"demo stopped (%d msec elapsed)", lastmillis - starttime);
	stop();
	loopv(players) zapdynent(players[i]);
	disconnect(0, 0);
};
}

VAR(demoplaybackspeed, 10, 100, 1000);
int
scaletime(int t)
{
	return (int)(t * (100.0f / demoplaybackspeed)) + starttime;
};
}

void
readdemotime()
{
	if (gzeof(f) || (playbacktime = gzgeti()) == -1) {
		stopreset();
		return;
	};
	}
	playbacktime = scaletime(playbacktime);
};
}

void
startdemo()
{
	democlientnum = gzgeti();
	demoplayback = true;
	starttime = lastmillis;
	conoutf("now playing demo");
	conoutf(@"now playing demo");
	dynent *d = getclient(democlientnum);
	assert(d);
	*d = *player1;
	readdemotime();
};
}

VAR(demodelaymsec, 0, 120, 500);

void
catmulrom(
    vec &z, vec &a, vec &b, vec &c, float s, vec &dest) // spline interpolation
{
374
375
376
377
378
379
380
381

382
383
384
385
386
387
388
374
375
376
377
378
379
380

381
382
383
384
385
386
387
388







-
+







void
demoplaybackstep()
{
	while (demoplayback && lastmillis >= playbacktime) {
		int len = gzgeti();
		if (len < 1 || len > MAXTRANS) {
			conoutf(
			    "error: huge packet during demo play (%d)", len);
			    @"error: huge packet during demo play (%d)", len);
			stopreset();
			return;
		};
		uchar buf[MAXTRANS];
		gzread(f, buf, len);
		localservertoclient(buf, len); // update game state

479
480
481
482
483
484
485
486

487
488
489
490
491
492
493
494
479
480
481
482
483
484
485

486
487
488
489
490
491
492
493
494







-
+








void
stopn()
{
	if (demoplayback)
		stopreset();
	else
		stop();
	conoutf("demo stopped");
	conoutf(@"demo stopped");
};

COMMAND(record, ARG_1STR);
COMMAND(demo, ARG_1STR);
COMMANDN(stop, stopn, ARG_NONE);

COMMAND(savegame, ARG_1STR);
COMMAND(loadgame, ARG_1STR);

Modified src/serverbrowser.mm from [01d1ff60c2] to [164c8571dd].

302
303
304
305
306
307
308
309

310
311
312
313
314
315
316
302
303
304
305
306
307
308

309
310
311
312
313
314
315
316







-
+







updatefrommaster()
{
	const int MAXUPD = 32000;
	uchar buf[MAXUPD];
	uchar *reply = retrieveservers(buf, MAXUPD);
	if (!*reply || strstr((char *)reply, "<html>") ||
	    strstr((char *)reply, "<HTML>"))
		conoutf("master server not replying");
		conoutf(@"master server not replying");
	else {
		servers.setsize(0);
		execute((char *)reply);
	};
	servermenu();
};

Modified src/sound.mm from [ebc1ddf348] to [da17c5965d].

59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82







-
+








-
+







void
initsound()
{
	memset(soundlocs, 0, sizeof(soundloc) * MAXCHAN);
#ifdef USE_MIXER
	if (Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen) <
	    0) {
		conoutf("sound init failed (SDL_mixer): %s",
		conoutf(@"sound init failed (SDL_mixer): %s",
		    (size_t)Mix_GetError());
		nosound = true;
	};
	Mix_AllocateChannels(MAXCHAN);
#else
	if (FSOUND_GetVersion() < FMOD_VERSION)
		fatal("old FMOD dll");
	if (!FSOUND_Init(SOUNDFREQ, MAXCHAN, FSOUND_INIT_GLOBALFOCUS)) {
		conoutf("sound init failed (FMOD): %d", FSOUND_GetError());
		conoutf(@"sound init failed (FMOD): %d", FSOUND_GetError());
		nosound = true;
	};
#endif
};

void
music(char *name)
102
103
104
105
106
107
108
109

110
111
112
113
114
115
116
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116







-
+







			int chan = FSOUND_Stream_Play(FSOUND_FREE, stream);
			if (chan >= 0) {
				FSOUND_SetVolume(
				    chan, (musicvol * MAXVOL) / 255);
				FSOUND_SetPaused(chan, false);
			};
		} else {
			conoutf("could not play music: %s", sn);
			conoutf(@"could not play music: %s", sn);
		};
#endif
	};
};

COMMAND(music, ARG_1STR);

224
225
226
227
228
229
230
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
253







-
+














-
+







	else
		soundsatonce = 1;
	lastsoundmillis = lastmillis;
	if (soundsatonce > 5)
		return; // avoid bursts of sounds with heavy packetloss and in
		        // sp
	if (n < 0 || n >= samples.length()) {
		conoutf("unregistered sound: %d", n);
		conoutf(@"unregistered sound: %d", n);
		return;
	};

	if (!samples[n]) {
		sprintf_sd(buf)("packages/sounds/%s.wav", snames[n]);

#ifdef USE_MIXER
		samples[n] = Mix_LoadWAV(path(buf));
#else
		samples[n] =
		    FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0);
#endif

		if (!samples[n]) {
			conoutf("failed to load sample: %s", buf);
			conoutf(@"failed to load sample: %s", buf);
			return;
		};
	};

#ifdef USE_MIXER
	int chan = Mix_PlayChannel(-1, samples[n], 0);
#else

Modified src/weapon.mm from [ce8dd423e7] to [2770740083].

46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60







-
+







	else if (s != GUN_RIFLE && player1->ammo[GUN_RIFLE])
		s = GUN_RIFLE;
	else
		s = GUN_FIST;
	if (s != player1->gunselect)
		playsoundc(S_WEAPLOAD);
	player1->gunselect = s;
	// conoutf("%s selected", (int)guns[s].name);
	// conoutf(@"%s selected", (int)guns[s].name);
};

int
reloadtime(int gun)
{
	return guns[gun].attackdelay;
};

Modified src/world.mm from [cb52ab13f9] to [0157413baa].

289
290
291
292
293
294
295
296

297
298
299
300

301
302
303
304
305
306
307
308
309
310
311

312
313
314
315
316
317
318
289
290
291
292
293
294
295

296
297
298
299

300
301
302
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318







-
+



-
+










-
+







};

void
delent()
{
	int e = closestent();
	if (e < 0) {
		conoutf("no more entities");
		conoutf(@"no more entities");
		return;
	};
	int t = ents[e].type;
	conoutf("%s entity deleted", entnames[t]);
	conoutf(@"%s entity deleted", entnames[t]);
	ents[e].type = NOTUSED;
	addmsg(1, 10, SV_EDITENT, e, NOTUSED, 0, 0, 0, 0, 0, 0, 0);
	if (t == LIGHT)
		calclight();
};

int
findtype(char *what)
{
	loopi(MAXENTTYPES) if (strcmp(what, entnames[i]) == 0) return i;
	conoutf("unknown entity type \"%s\"", what);
	conoutf(@"unknown entity type \"%s\"", what);
	return NOTUSED;
}

entity *
newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v4)
{
	int type = findtype(what);

Modified src/worldio.mm from [4ce3f10da1] to [e1e98ece3c].

124
125
126
127
128
129
130
131

132
133
134
135
136

137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
124
125
126
127
128
129
130

131
132
133
134
135

136
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152







-
+




-
+








-
+







void
writemap(char *mname, int msize, uchar *mdata)
{
	setnames(mname);
	backup(cgzname, bakname);
	FILE *f = fopen(cgzname, "wb");
	if (!f) {
		conoutf("could not write map to %s", cgzname);
		conoutf(@"could not write map to %s", cgzname);
		return;
	};
	fwrite(mdata, 1, msize, f);
	fclose(f);
	conoutf("wrote map %s as file %s", mname, cgzname);
	conoutf(@"wrote map %s as file %s", mname, cgzname);
}

uchar *
readmap(char *mname, int *msize)
{
	setnames(mname);
	uchar *mdata = (uchar *)loadfile(cgzname, msize);
	if (!mdata) {
		conoutf("could not read map %s", cgzname);
		conoutf(@"could not read map %s", cgzname);
		return NULL;
	};
	return mdata;
}

// save map as .cgz file. uses 2 layers of compression: first does simple
// run-length encoding and leaves out data for certain kinds of cubes, then zlib
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175







-
+







	toptimize();
	if (!*mname)
		mname = getclientmap();
	setnames(mname);
	backup(cgzname, bakname);
	gzFile f = gzopen(cgzname, "wb9");
	if (!f) {
		conoutf("could not write map to %s", cgzname);
		conoutf(@"could not write map to %s", cgzname);
		return;
	};
	hdr.version = MAPVERSION;
	hdr.numents = 0;
	loopv(ents) if (ents[i].type != NOTUSED) hdr.numents++;
	header tmp = hdr;
	endianswap(&tmp.version, sizeof(int), 4);
233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
233
234
235
236
237
238
239

240
241
242
243
244
245
246
247
248
249
250
251
252
253

254
255
256
257
258
259
260
261







-
+













-
+







				gzputc(f, s->tag);
			};
		};
		t = s;
	};
	spurge;
	gzclose(f);
	conoutf("wrote map file %s", cgzname);
	conoutf(@"wrote map file %s", cgzname);
	settagareas();
};

void
load_world(char *mname) // still supports all map formats that have existed
                        // since the earliest cube betas!
{
	stopifrecording();
	cleardlights();
	pruneundos();
	setnames(mname);
	gzFile f = gzopen(cgzname, "rb9");
	if (!f) {
		conoutf("could not read map %s", cgzname);
		conoutf(@"could not read map %s", cgzname);
		return;
	};
	gzread(f, &hdr, sizeof(header) - sizeof(int) * 16);
	endianswap(&hdr.version, sizeof(int), 4);
	if (strncmp(hdr.head, "CUBE", 4) != 0)
		fatal("while reading map: header malformatted");
	if (hdr.version > MAPVERSION)
353
354
355
356
357
358
359
360

361
362

363
364
365
366
367
368
369
353
354
355
356
357
358
359

360
361

362
363
364
365
366
367
368
369







-
+

-
+







			texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1;
	};
	gzclose(f);
	calclight();
	settagareas();
	int xs, ys;
	loopi(256) if (texuse) lookuptexture(i, xs, ys);
	conoutf("read map %s (%d milliseconds)", cgzname,
	conoutf(@"read map %s (%d milliseconds)", cgzname,
	    SDL_GetTicks() - lastmillis);
	conoutf("%s", hdr.maptitle);
	conoutf(@"%s", hdr.maptitle);
	startmap(mname);
	loopl(256)
	{
		sprintf_sd(aliasname)(
		    "level_trigger_%d", l); // can this be done smarter?
		if (identexists(aliasname))
			alias(aliasname, "");