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) \
57 if (sf & (Flags)) \
58 Write##Type(MSG_ENTITY, this.Name);
59
60#define MSLE(Name, Fields) \
61 else if (this.classname == #Name) \
62 { \
63 if (sf & MINIG_SF_CREATE) \
64 WriteString(MSG_ENTITY, this.owner.netname); \
65 Fields \
66 }
67
68// Send an entity to a client
69// only use on minigame entities or entities with a minigame owner
70bool minigame_SendEntity(entity this, entity to, int sf)
71{
72 WriteHeader(MSG_ENTITY, ENT_CLIENT_MINIGAME);
74
75 if ( sf & MINIG_SF_CREATE )
76 {
79 }
80
81 entity minigame_ent = this.owner;
82
83 if ( this.classname == "minigame" )
84 {
85 minigame_ent = this;
86
87 if ( sf & MINIG_SF_CREATE )
88 WriteString(MSG_ENTITY,this.descriptor.netname);
89
90 if ( sf & MINIG_SF_UPDATE )
92 }
93 else if ( this.classname == "minigame_player" )
94 {
95 if ( sf & MINIG_SF_CREATE )
96 {
97 WriteString(MSG_ENTITY,this.owner.netname);
99 }
100 if ( sf & MINIG_SF_UPDATE )
102 }
104
105 minigame_ent.minigame_event(minigame_ent,"network_send",this,sf);
106
107 return true;
108
109}
110#undef FIELD
111#undef MSLE
112
113// Force resend all minigame entities
115{
116 minigame.SendFlags = MINIG_SF_ALL;
117 entity e = NULL;
118 while (( e = findentity(e,owner,minigame) ))
119 {
120 e.SendFlags = MINIG_SF_ALL;
121 }
122}
123
125{
126 entity e;
127 for ( e = this.owner.minigame_players; e != NULL; e = e.list_next )
128 if ( e.minigame_players == client )
129 return true;
130 return false;
131}
132
133int minigame_addplayer(entity minigame_session, entity player)
134{
135 if ( CS(player).active_minigame )
136 {
137 if ( CS(player).active_minigame == minigame_session )
138 return 0;
139 minigame_rmplayer(CS(player).active_minigame,player);
140 }
141 entity player_pointer = new(minigame_player);
142 int mgteam = minigame_session.minigame_event(minigame_session,"join",player,player_pointer);
143
144 if ( mgteam )
145 {
146 player_pointer.owner = minigame_session;
147 player_pointer.minigame_players = player;
148 player_pointer.team = mgteam;
149 player_pointer.list_next = minigame_session.minigame_players;
150 minigame_session.minigame_players = player_pointer;
151 CS(player).active_minigame = minigame_session;
152 player.minigame_players = player_pointer;
153 setcefc(player_pointer, minigame_CheckSend);
154 Net_LinkEntity(player_pointer, false, 0, minigame_SendEntity);
155
157 PutObserverInServer(player, true, true);
160
161 minigame_resend(minigame_session);
162 }
163 else { delete(player_pointer); }
164 GameLogEcho(strcat(":minigame:join",(mgteam?"":"fail"),":",minigame_session.netname,":",
165 ftos(etof(player)),":",player.netname));
166
167 return mgteam;
168}
169
170entity start_minigame(entity player, string minigame )
171{
172 if ( !autocvar_sv_minigames || !IS_REAL_CLIENT(player) )
173 return NULL;
174
175 entity e = minigame_get_descriptor(minigame);
176 if ( e )
177 {
178 entity minig = new(minigame);
179 minig.netname = strzone(strcat(e.netname,"_",ftos(etof(minig))));
180 minig.descriptor = e;
181 minig.minigame_event = e.minigame_event;
182 minig.minigame_event(minig,"start");
183 GameLogEcho(strcat(":minigame:start:",minig.netname));
184 if ( ! minigame_addplayer(minig,player) )
185 {
186 LOG_TRACE("Minigame ",minig.netname," rejected the first player join!");
187 end_minigame(minig);
188 return NULL;
189 }
190 Net_LinkEntity(minig, false, 0, minigame_SendEntity);
191
192 if ( !minigame_sessions )
193 minigame_sessions = minig;
194 else
195 {
196 minigame_sessions.owner = minig;
197 minig.list_next = minigame_sessions;
198 minigame_sessions = minig;
199 }
200 return minig;
201 }
202
203 return NULL;
204}
205
206entity join_minigame(entity player, string game_id )
207{
208 if ( !autocvar_sv_minigames || !IS_REAL_CLIENT(player) )
209 return NULL;
210
211 entity minig;
212 for ( minig = minigame_sessions; minig != NULL; minig = minig.list_next )
213 {
214 if ( minig.netname == game_id )
215 if ( minigame_addplayer(minig,player) )
216 return minig;
217 }
218
219 return NULL;
220}
221
223{
224 entity minig = CS(player).active_minigame;
225
226 if ( minig && minig.classname == "minigame" )
227 minigame_rmplayer(minig,player);
228}
229
230void end_minigame(entity minigame_session)
231{
232 if ( minigame_session.owner )
233 minigame_session.owner.list_next = minigame_session.list_next;
234 else
235 minigame_sessions = minigame_session.list_next;
236
237 minigame_session.minigame_event(minigame_session,"end");
238 GameLogEcho(strcat(":minigame:end:",minigame_session.netname));
239
240
241 entity e = NULL;
242 while( (e = findentity(e, owner, minigame_session)) )
243 if ( e.minigame_autoclean )
244 {
245 LOG_TRACE("SV Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")");
246 delete(e);
247 }
248
249 entity p;
250 for ( e = minigame_session.minigame_players; e != NULL; e = p )
251 {
252 p = e.list_next;
253 player_clear_minigame(e.minigame_players);
254 delete(e);
255 }
256
257 strfree(minigame_session.netname);
258 delete(minigame_session);
259}
260
262{
263 while ( minigame_sessions )
264 {
266 }
267}
268
269string invite_minigame(entity inviter, entity player)
270{
271 if ( !inviter || !CS(inviter).active_minigame )
272 return "Invalid minigame";
273 if ( VerifyClientEntity(player, true, false) <= 0 )
274 return "Invalid player";
275 if ( inviter == player )
276 return "You can't invite yourself";
278 return "You can't invite a banned player";
279 if ( CS(player).active_minigame == CS(inviter).active_minigame )
280 return strcat(player.netname," is already playing");
281
282 Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_MINIGAME_INVITE,
283 CS(inviter).active_minigame.netname, inviter.netname );
284
285 GameLogEcho(strcat(":minigame:invite:",CS(inviter).active_minigame.netname,":",
286 ftos(etof(player)),":",player.netname));
287
288 return "";
289}
290
292{
293 if ( ! CS(client).active_minigame )
294 return NULL;
295 entity e;
296 for ( e = CS(client).active_minigame.minigame_players; e; e = e.list_next )
297 if ( e.minigame_players == client )
298 return e;
299 return NULL;
300}
301
303{
304 if (!CS(this).active_minigame) return false;
306 if ( imp && CS(this).active_minigame && e )
307 {
308 return CS(this).active_minigame.minigame_event(CS(this).active_minigame,"impulse",e,imp);
309 }
310 return false;
311}
312
313// this macro exists only to shorten code lines
314#define MINIGAME_CMD(cmd_id) MINIGAME_COMMON_CMD[MINIGAME_COMMON_CMD_ID_##cmd_id]
315
316void ClientCommand_minigame(entity caller, int request, int argc, string command)
317{
319 {
320 sprint(caller,"Minigames are not enabled!\n");
321 return;
322 }
323
325 {
326 Send_Notification(NOTIF_ONE_ONLY, caller, MSG_CENTER, CENTER_JOIN_PLAYBAN);
327 sprint(caller, "You aren't allowed to play minigames because you are banned from them in this server.\n");
328 return;
329 }
330
331 if (request == CMD_REQUEST_COMMAND )
332 {
333 string minig_cmd = argv(1);
334 if ( minig_cmd == MINIGAME_CMD(CREATE) && argc > 2 )
335 {
336 entity minig = start_minigame(caller, argv(2));
337 if ( minig )
338 sprint(caller,"Created minigame session: ",minig.netname,"\n");
339 else
340 sprint(caller,"Cannot start minigame session!\n");
341 return;
342 }
343 else if ( minig_cmd == MINIGAME_CMD(JOIN) && argc > 2 )
344 {
345 entity minig = join_minigame(caller, argv(2));
346 if ( minig )
347 sprint(caller,"Joined: ",minig.netname,"\n");
348 else
349 {
350 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_JOIN_PREVENT_MINIGAME);
351 sprint(caller,"Cannot join given minigame session!\n");
352 }
353 return;
354 }
355 else if ( minig_cmd == MINIGAME_CMD(LIST) )
356 {
357 FOREACH(Minigames, true, sprint(caller, it.netname, " (", it.message, ") ", "\n"));
358 return;
359 }
360 else if ( minig_cmd == MINIGAME_CMD(LIST_SESSIONS) )
361 {
362 entity e;
363 for ( e = minigame_sessions; e != NULL; e = e.list_next )
364 sprint(caller,e.netname,"\n");
365 return;
366 }
367 else if ( minig_cmd == MINIGAME_CMD(END) || minig_cmd == MINIGAME_CMD(PART) )
368 {
369 if ( CS(caller).active_minigame )
370 {
371 part_minigame(caller);
372 sprint(caller,"Left minigame session\n");
373 }
374 else
375 sprint(caller,"You aren't playing any minigame...\n");
376 return;
377 }
378 else if ( minig_cmd == MINIGAME_CMD(INVITE) && argc > 2 )
379 {
380 if ( CS(caller).active_minigame )
381 {
382 entity client = GetIndexedEntity(argc, 2);
383 string error = invite_minigame(caller,client);
384 if ( error == "" )
385 {
386 sprint(caller,"You have invited ",client.netname,
387 " to join your game of ", CS(caller).active_minigame.descriptor.message, "\n");
388 }
389 else
390 sprint(caller,"Could not invite: ", error, ".\n");
391 }
392 else
393 sprint(caller,"You aren't playing any minigame...\n");
394 return;
395 }
396 else if ( CS(caller).active_minigame )
397 {
398 entity e = minigame_find_player(caller);
399 string subcommand = substring(command,argv_end_index(0),-1);
400 int arg_c = tokenize_console(subcommand);
401 if ( CS(caller).active_minigame.minigame_event(CS(caller).active_minigame,"cmd",e,arg_c,subcommand) )
402 return;
403
404 }
405 else sprint(caller,strcat("Wrong command:^1 ",command,"\n"));
406 }
407
408 sprint(caller, "\nUsage:^3 cmd minigame create <minigame>\n");
409 sprint(caller, " Start a new minigame session\n");
410 sprint(caller, "Usage:^3 cmd minigame join <session>\n");
411 sprint(caller, " Join an exising minigame session\n");
412 sprint(caller, "Usage:^3 cmd minigame list\n");
413 sprint(caller, " List available minigames\n");
414 sprint(caller, "Usage:^3 cmd minigame list-sessions\n");
415 sprint(caller, " List available minigames sessions\n");
416 sprint(caller, "Usage:^3 cmd minigame part|end\n");
417 sprint(caller, " Leave the current minigame\n");
418 sprint(caller, "Usage:^3 cmd minigame invite <player>\n");
419 sprint(caller, " Invite the given player to join you in a minigame\n");
420}
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:242
string classname
#define argv_end_index
#define tokenize_console
WriteString(chan, ent.netname)
WriteByte(chan, ent.angles.y/DEC_FACTOR)
void GameLogEcho(string s)
Definition gamelog.qc:15
#define FOREACH(list, cond, body)
Definition iter.qh:19
const int MSG_ENTITY
Definition net.qh:156
#define WriteHeader(to, id)
Definition net.qh:265
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
#define LOG_TRACE(...)
Definition log.qh:74
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 sprint(float clientnum, string text,...)
void WriteShort(float data, float dest, float desto)
string ftos(float f)
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:136
const int MOVETYPE_FLY_WORLDONLY
Definition movetypes.qh:147
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:1500
#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:1047
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:57
#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:354
@ TEAM_FORCE_DEFAULT
Don't force any team.
Definition teamplay.qh:153
@ TEAM_FORCE_SPECTATOR
Force the player to spectator team.
Definition teamplay.qh:152
#define IS_OBSERVER(v)
Definition utils.qh:11
#define IS_REAL_CLIENT(v)
Definition utils.qh:17