Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
sv_minigames.qc
Go to the documentation of this file.
1#include "sv_minigames.qh"
2
4#include <server/gamelog.qh>
5
7{
8 CS(player).active_minigame = NULL;
9 player.minigame_players = NULL;
10 if ( IS_PLAYER(player) )
12 else
15}
16
17void minigame_rmplayer(entity minigame_session, entity player)
18{
19 entity e;
20 entity p = minigame_session.minigame_players;
21
22 if ( p.minigame_players == player )
23 {
24 if ( p.list_next == NULL )
25 {
26 end_minigame(minigame_session);
27 return;
28 }
29 minigame_session.minigame_event(minigame_session,"part",player);
30 GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":",
31 ftos(etof(player)),":",player.netname));
32 minigame_session.minigame_players = p.list_next;
33 delete( p );
35 }
36 else
37 {
38 for ( e = p.list_next; e != NULL; e = e.list_next )
39 {
40 if ( e.minigame_players == player )
41 {
42 minigame_session.minigame_event(minigame_session,"part",player);
43 GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":",
44 ftos(etof(player)),":",player.netname));
45 p.list_next = e.list_next;
46 delete(e);
48 return;
49 }
50 p = e;
51 }
52 }
53}
54
55
56#define FIELD(Flags, Type,Name) if ( sf & (Flags) ) Write##Type(MSG_ENTITY, this.Name);
57#define MSLE(Name,Fields) \
58 else if ( this.classname == #Name ) { \
59 if ( sf & MINIG_SF_CREATE ) WriteString(MSG_ENTITY,this.owner.netname); \
60 Fields }
61
62// Send an entity to a client
63// only use on minigame entities or entities with a minigame owner
64bool minigame_SendEntity(entity this, entity to, int sf)
65{
66 WriteHeader(MSG_ENTITY, ENT_CLIENT_MINIGAME);
68
69 if ( sf & MINIG_SF_CREATE )
70 {
73 }
74
75 entity minigame_ent = this.owner;
76
77 if ( this.classname == "minigame" )
78 {
79 minigame_ent = this;
80
81 if ( sf & MINIG_SF_CREATE )
82 WriteString(MSG_ENTITY,this.descriptor.netname);
83
84 if ( sf & MINIG_SF_UPDATE )
86 }
87 else if ( this.classname == "minigame_player" )
88 {
89 if ( sf & MINIG_SF_CREATE )
90 {
91 WriteString(MSG_ENTITY,this.owner.netname);
93 }
94 if ( sf & MINIG_SF_UPDATE )
96 }
98
99 minigame_ent.minigame_event(minigame_ent,"network_send",this,sf);
100
101 return true;
102
103}
104#undef FIELD
105#undef MSLE
106
107// Force resend all minigame entities
109{
110 minigame.SendFlags = MINIG_SF_ALL;
111 entity e = NULL;
112 while (( e = findentity(e,owner,minigame) ))
113 {
114 e.SendFlags = MINIG_SF_ALL;
115 }
116}
117
119{
120 entity e;
121 for ( e = this.owner.minigame_players; e != NULL; e = e.list_next )
122 if ( e.minigame_players == client )
123 return true;
124 return false;
125}
126
127int minigame_addplayer(entity minigame_session, entity player)
128{
129 if ( CS(player).active_minigame )
130 {
131 if ( CS(player).active_minigame == minigame_session )
132 return 0;
133 minigame_rmplayer(CS(player).active_minigame,player);
134 }
135 entity player_pointer = new(minigame_player);
136 int mgteam = minigame_session.minigame_event(minigame_session,"join",player,player_pointer);
137
138 if ( mgteam )
139 {
140 player_pointer.owner = minigame_session;
141 player_pointer.minigame_players = player;
142 player_pointer.team = mgteam;
143 player_pointer.list_next = minigame_session.minigame_players;
144 minigame_session.minigame_players = player_pointer;
145 CS(player).active_minigame = minigame_session;
146 player.minigame_players = player_pointer;
147 setcefc(player_pointer, minigame_CheckSend);
148 Net_LinkEntity(player_pointer, false, 0, minigame_SendEntity);
149
151 PutObserverInServer(player, true, true);
154
155 minigame_resend(minigame_session);
156 }
157 else { delete(player_pointer); }
158 GameLogEcho(strcat(":minigame:join",(mgteam?"":"fail"),":",minigame_session.netname,":",
159 ftos(etof(player)),":",player.netname));
160
161 return mgteam;
162}
163
164entity start_minigame(entity player, string minigame )
165{
166 if ( !autocvar_sv_minigames || !IS_REAL_CLIENT(player) )
167 return NULL;
168
169 entity e = minigame_get_descriptor(minigame);
170 if ( e )
171 {
172 entity minig = new(minigame);
173 minig.netname = strzone(strcat(e.netname,"_",ftos(etof(minig))));
174 minig.descriptor = e;
175 minig.minigame_event = e.minigame_event;
176 minig.minigame_event(minig,"start");
177 GameLogEcho(strcat(":minigame:start:",minig.netname));
178 if ( ! minigame_addplayer(minig,player) )
179 {
180 LOG_TRACE("Minigame ",minig.netname," rejected the first player join!");
181 end_minigame(minig);
182 return NULL;
183 }
184 Net_LinkEntity(minig, false, 0, minigame_SendEntity);
185
186 if ( !minigame_sessions )
187 minigame_sessions = minig;
188 else
189 {
190 minigame_sessions.owner = minig;
191 minig.list_next = minigame_sessions;
192 minigame_sessions = minig;
193 }
194 return minig;
195 }
196
197 return NULL;
198}
199
200entity join_minigame(entity player, string game_id )
201{
202 if ( !autocvar_sv_minigames || !IS_REAL_CLIENT(player) )
203 return NULL;
204
205 entity minig;
206 for ( minig = minigame_sessions; minig != NULL; minig = minig.list_next )
207 {
208 if ( minig.netname == game_id )
209 if ( minigame_addplayer(minig,player) )
210 return minig;
211 }
212
213 return NULL;
214}
215
217{
218 entity minig = CS(player).active_minigame;
219
220 if ( minig && minig.classname == "minigame" )
221 minigame_rmplayer(minig,player);
222}
223
224void end_minigame(entity minigame_session)
225{
226 if ( minigame_session.owner )
227 minigame_session.owner.list_next = minigame_session.list_next;
228 else
229 minigame_sessions = minigame_session.list_next;
230
231 minigame_session.minigame_event(minigame_session,"end");
232 GameLogEcho(strcat(":minigame:end:",minigame_session.netname));
233
234
235 entity e = NULL;
236 while( (e = findentity(e, owner, minigame_session)) )
237 if ( e.minigame_autoclean )
238 {
239 LOG_TRACE("SV Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")");
240 delete(e);
241 }
242
243 entity p;
244 for ( e = minigame_session.minigame_players; e != NULL; e = p )
245 {
246 p = e.list_next;
247 player_clear_minigame(e.minigame_players);
248 delete(e);
249 }
250
251 strfree(minigame_session.netname);
252 delete(minigame_session);
253}
254
256{
257 while ( minigame_sessions )
258 {
260 }
261}
262
263string invite_minigame(entity inviter, entity player)
264{
265 if ( !inviter || !CS(inviter).active_minigame )
266 return "Invalid minigame";
267 if ( VerifyClientEntity(player, true, false) <= 0 )
268 return "Invalid player";
269 if ( inviter == player )
270 return "You can't invite yourself";
272 return "You can't invite a banned player";
273 if ( CS(player).active_minigame == CS(inviter).active_minigame )
274 return strcat(player.netname," is already playing");
275
276 Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_MINIGAME_INVITE,
277 CS(inviter).active_minigame.netname, inviter.netname );
278
279 GameLogEcho(strcat(":minigame:invite:",CS(inviter).active_minigame.netname,":",
280 ftos(etof(player)),":",player.netname));
281
282 return "";
283}
284
286{
287 if ( ! CS(client).active_minigame )
288 return NULL;
289 entity e;
290 for ( e = CS(client).active_minigame.minigame_players; e; e = e.list_next )
291 if ( e.minigame_players == client )
292 return e;
293 return NULL;
294}
295
297{
298 if (!CS(this).active_minigame) return false;
300 if ( imp && CS(this).active_minigame && e )
301 {
302 return CS(this).active_minigame.minigame_event(CS(this).active_minigame,"impulse",e,imp);
303 }
304 return false;
305}
306
307// this macro exists only to shorten code lines
308#define MINIGAME_CMD(cmd_id) MINIGAME_COMMON_CMD[MINIGAME_COMMON_CMD_ID_##cmd_id]
309
310void ClientCommand_minigame(entity caller, int request, int argc, string command)
311{
313 {
314 sprint(caller,"Minigames are not enabled!\n");
315 return;
316 }
317
319 {
320 Send_Notification(NOTIF_ONE_ONLY, caller, MSG_CENTER, CENTER_JOIN_PLAYBAN);
321 sprint(caller, "You aren't allowed to play minigames because you are banned from them in this server.\n");
322 return;
323 }
324
325 if (request == CMD_REQUEST_COMMAND )
326 {
327 string minig_cmd = argv(1);
328 if ( minig_cmd == MINIGAME_CMD(CREATE) && argc > 2 )
329 {
330 entity minig = start_minigame(caller, argv(2));
331 if ( minig )
332 sprint(caller,"Created minigame session: ",minig.netname,"\n");
333 else
334 sprint(caller,"Cannot start minigame session!\n");
335 return;
336 }
337 else if ( minig_cmd == MINIGAME_CMD(JOIN) && argc > 2 )
338 {
339 entity minig = join_minigame(caller, argv(2));
340 if ( minig )
341 sprint(caller,"Joined: ",minig.netname,"\n");
342 else
343 {
344 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_JOIN_PREVENT_MINIGAME);
345 sprint(caller,"Cannot join given minigame session!\n");
346 }
347 return;
348 }
349 else if ( minig_cmd == MINIGAME_CMD(LIST) )
350 {
351 FOREACH(Minigames, true, sprint(caller, it.netname, " (", it.message, ") ", "\n"));
352 return;
353 }
354 else if ( minig_cmd == MINIGAME_CMD(LIST_SESSIONS) )
355 {
356 entity e;
357 for ( e = minigame_sessions; e != NULL; e = e.list_next )
358 sprint(caller,e.netname,"\n");
359 return;
360 }
361 else if ( minig_cmd == MINIGAME_CMD(END) || minig_cmd == MINIGAME_CMD(PART) )
362 {
363 if ( CS(caller).active_minigame )
364 {
365 part_minigame(caller);
366 sprint(caller,"Left minigame session\n");
367 }
368 else
369 sprint(caller,"You aren't playing any minigame...\n");
370 return;
371 }
372 else if ( minig_cmd == MINIGAME_CMD(INVITE) && argc > 2 )
373 {
374 if ( CS(caller).active_minigame )
375 {
376 entity client = GetIndexedEntity(argc, 2);
377 string error = invite_minigame(caller,client);
378 if ( error == "" )
379 {
380 sprint(caller,"You have invited ",client.netname,
381 " to join your game of ", CS(caller).active_minigame.descriptor.message, "\n");
382 }
383 else
384 sprint(caller,"Could not invite: ", error, ".\n");
385 }
386 else
387 sprint(caller,"You aren't playing any minigame...\n");
388 return;
389 }
390 else if ( CS(caller).active_minigame )
391 {
392 entity e = minigame_find_player(caller);
393 string subcommand = substring(command,argv_end_index(0),-1);
394 int arg_c = tokenize_console(subcommand);
395 if ( CS(caller).active_minigame.minigame_event(CS(caller).active_minigame,"cmd",e,arg_c,subcommand) )
396 return;
397
398 }
399 else sprint(caller,strcat("Wrong command:^1 ",command,"\n"));
400 }
401
402 sprint(caller, "\nUsage:^3 cmd minigame create <minigame>\n");
403 sprint(caller, " Start a new minigame session\n");
404 sprint(caller, "Usage:^3 cmd minigame join <session>\n");
405 sprint(caller, " Join an exising minigame session\n");
406 sprint(caller, "Usage:^3 cmd minigame list\n");
407 sprint(caller, " List available minigames\n");
408 sprint(caller, "Usage:^3 cmd minigame list-sessions\n");
409 sprint(caller, " List available minigames sessions\n");
410 sprint(caller, "Usage:^3 cmd minigame part|end\n");
411 sprint(caller, " Leave the current minigame\n");
412 sprint(caller, "Usage:^3 cmd minigame invite <player>\n");
413 sprint(caller, " Invite the given player to join you in a minigame\n");
414}
string autocvar_g_playban_list
Definition banning.qh:14
bool autocvar_g_playban_minigames
Definition banning.qh:15
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
entity active_minigame
string netname
Definition powerups.qc:20
entity owner
Definition main.qh:87
int team
Definition main.qh:188
const int CMD_REQUEST_COMMAND
Definition command.qh:3
#define IS_PLAYER(s)
Definition player.qh:243
string classname
#define argv_end_index
#define tokenize_console
void GameLogEcho(string s)
Definition gamelog.qc:15
#define FOREACH(list, cond, body)
Definition iter.qh:19
const int MSG_ENTITY
Definition net.qh:115
#define WriteHeader(to, id)
Definition net.qh:221
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
#define LOG_TRACE(...)
Definition log.qh:76
entity findentity(entity start,.entity field, entity match)
string substring(string s, float start, float length)
void WriteLong(float data, float dest, float desto)
void WriteString(string data, float dest, float desto)
void sprint(float clientnum, string text,...)
void WriteShort(float data, float dest, float desto)
string ftos(float f)
void WriteByte(float data, float dest, float desto)
string strzone(string s)
string argv(float n)
#define MINIGAME_SIMPLELINKED_ENTITIES
Set up automatic entity read/write functionality To ensure that everything is handled automatically,...
Definition all.qh:100
int msle_id(string class_name)
Definition minigames.qc:99
entity minigame_get_descriptor(string id)
Definition minigames.qc:5
int minigame_flags
Definition minigames.qh:103
const int MINIG_SF_UPDATE
Definition minigames.qh:109
const int MINIG_SF_ALL
Definition minigames.qh:112
const int MINIG_SF_CREATE
Definition minigames.qh:108
entity descriptor
For minigame sessions: minigame descriptor object.
Definition minigames.qh:45
#define etof(e)
Definition misc.qh:25
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_WALK
Definition movetypes.qh:132
const int MOVETYPE_FLY_WORLDONLY
Definition movetypes.qh:143
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
#define setcefc(e, f)
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
Definition client.qc:261
bool PlayerInList(entity player, string list)
Definition client.qc:1045
entity GetIndexedEntity(int argc, int start_index)
Definition common.qc:82
int VerifyClientEntity(entity client, bool must_be_real, bool must_be_bots)
Definition common.qc:47
int int int imp
Definition impulse.qc:90
ClientState CS(Client this)
Definition state.qh:47
#define strfree(this)
Definition string.qh:59
#define MINIGAME_CMD(cmd_id)
int minigame_addplayer(entity minigame_session, entity player)
bool MinigameImpulse(entity this, int imp)
void player_clear_minigame(entity player)
bool minigame_CheckSend(entity this, entity client)
void minigame_rmplayer(entity minigame_session, entity player)
entity join_minigame(entity player, string game_id)
Join an existing minigame session.
void end_minigames()
bool minigame_SendEntity(entity this, entity to, int sf)
void part_minigame(entity player)
void end_minigame(entity minigame_session)
entity start_minigame(entity player, string minigame)
Create a new minigame session.
string invite_minigame(entity inviter, entity player)
Invite a player to join in a minigame.
entity minigame_find_player(entity client)
void ClientCommand_minigame(entity caller, int request, int argc, string command)
void minigame_resend(entity minigame)
entity minigame_players
For minigame sessions: list of players For minigame_player: client entity.
entity minigame_sessions
bool autocvar_sv_minigames
bool autocvar_sv_minigames_observer
void Player_SetForcedTeamIndex(entity player, int team_index)
Sets the index of the forced team of the given player.
Definition teamplay.qc:323
@ TEAM_FORCE_DEFAULT
Don't force any team.
Definition teamplay.qh:138
@ TEAM_FORCE_SPECTATOR
Force the player to spectator team.
Definition teamplay.qh:137
#define IS_OBSERVER(v)
Definition utils.qh:11
#define IS_REAL_CLIENT(v)
Definition utils.qh:17