Xonotic
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) )
11  set_movetype(player, MOVETYPE_WALK);
12  else
15 }
16 
17 void 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 );
34  player_clear_minigame(player);
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);
47  player_clear_minigame(player);
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
64 bool minigame_SendEntity(entity this, entity to, int sf)
65 {
66  WriteHeader(MSG_ENTITY, ENT_CLIENT_MINIGAME);
67  WriteByte(MSG_ENTITY, sf);
68 
69  if ( sf & MINIG_SF_CREATE )
70  {
71  WriteShort(MSG_ENTITY,msle_id(this.classname));
72  WriteString(MSG_ENTITY,this.netname);
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 )
85  WriteLong(MSG_ENTITY,this.minigame_flags);
86  }
87  else if ( this.classname == "minigame_player" )
88  {
89  if ( sf & MINIG_SF_CREATE )
90  {
91  WriteString(MSG_ENTITY,this.owner.netname);
92  WriteLong(MSG_ENTITY,etof(this.minigame_players));
93  }
94  if ( sf & MINIG_SF_UPDATE )
95  WriteByte(MSG_ENTITY,this.team);
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
108 void minigame_resend(entity minigame)
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 
118 bool minigame_CheckSend(entity this, entity client)
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 
127 int 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 
164 entity 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 
200 entity 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 
216 void part_minigame(entity player )
217 {
218  entity minig = CS(player).active_minigame;
219 
220  if ( minig && minig.classname == "minigame" )
221  minigame_rmplayer(minig,player);
222 }
223 
224 void 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 
263 string 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";
271  if ( CS(player).active_minigame == CS(inviter).active_minigame )
272  return strcat(player.netname," is already playing");
273 
274  Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_MINIGAME_INVITE,
275  CS(inviter).active_minigame.netname, inviter.netname );
276 
277  GameLogEcho(strcat(":minigame:invite:",CS(inviter).active_minigame.netname,":",
278  ftos(etof(player)),":",player.netname));
279 
280  return "";
281 }
282 
284 {
285  if ( ! CS(client).active_minigame )
286  return NULL;
287  entity e;
288  for ( e = CS(client).active_minigame.minigame_players; e; e = e.list_next )
289  if ( e.minigame_players == client )
290  return e;
291  return NULL;
292 }
293 
294 bool MinigameImpulse(entity this, int imp)
295 {
296  if (!CS(this).active_minigame) return false;
297  entity e = minigame_find_player(this);
298  if ( imp && CS(this).active_minigame && e )
299  {
300  return CS(this).active_minigame.minigame_event(CS(this).active_minigame,"impulse",e,imp);
301  }
302  return false;
303 }
304 
305 
306 
307 void ClientCommand_minigame(entity caller, int request, int argc, string command)
308 {
309  if ( !autocvar_sv_minigames )
310  {
311  sprint(caller,"Minigames are not enabled!\n");
312  return;
313  }
314 
315  if (request == CMD_REQUEST_COMMAND )
316  {
317  string minig_cmd = argv(1);
318  if ( minig_cmd == "create" && argc > 2 )
319  {
320  entity minig = start_minigame(caller, argv(2));
321  if ( minig )
322  sprint(caller,"Created minigame session: ",minig.netname,"\n");
323  else
324  sprint(caller,"Cannot start minigame session!\n");
325  return;
326  }
327  else if ( minig_cmd == "join" && argc > 2 )
328  {
329  entity minig = join_minigame(caller, argv(2));
330  if ( minig )
331  sprint(caller,"Joined: ",minig.netname,"\n");
332  else
333  {
334  Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_JOIN_PREVENT_MINIGAME);
335  sprint(caller,"Cannot join given minigame session!\n");
336  }
337  return;
338  }
339  else if ( minig_cmd == "list" )
340  {
341  FOREACH(Minigames, true, sprint(caller, it.netname, " (", it.message, ") ", "\n"));
342  return;
343  }
344  else if ( minig_cmd == "list-sessions" )
345  {
346  entity e;
347  for ( e = minigame_sessions; e != NULL; e = e.list_next )
348  sprint(caller,e.netname,"\n");
349  return;
350  }
351  else if ( minig_cmd == "end" || minig_cmd == "part" )
352  {
353  if ( CS(caller).active_minigame )
354  {
355  part_minigame(caller);
356  sprint(caller,"Left minigame session\n");
357  }
358  else
359  sprint(caller,"You aren't playing any minigame...\n");
360  return;
361  }
362  else if ( minig_cmd == "invite" && argc > 2 )
363  {
364  if ( CS(caller).active_minigame )
365  {
366  entity client = GetIndexedEntity(argc, 2);
367  string error = invite_minigame(caller,client);
368  if ( error == "" )
369  {
370  sprint(caller,"You have invited ",client.netname,
371  " to join your game of ", CS(caller).active_minigame.descriptor.message, "\n");
372  }
373  else
374  sprint(caller,"Could not invite: ", error, ".\n");
375  }
376  else
377  sprint(caller,"You aren't playing any minigame...\n");
378  return;
379  }
380  else if ( CS(caller).active_minigame )
381  {
382  entity e = minigame_find_player(caller);
383  string subcommand = substring(command,argv_end_index(0),-1);
384  int arg_c = tokenize_console(subcommand);
385  if ( CS(caller).active_minigame.minigame_event(CS(caller).active_minigame,"cmd",e,arg_c,subcommand) )
386  return;
387 
388  }
389  else sprint(caller,strcat("Wrong command:^1 ",command,"\n"));
390  }
391 
392  sprint(caller, "\nUsage:^3 cmd minigame create <minigame>\n");
393  sprint(caller, " Start a new minigame session\n");
394  sprint(caller, "Usage:^3 cmd minigame join <session>\n");
395  sprint(caller, " Join an exising minigame session\n");
396  sprint(caller, "Usage:^3 cmd minigame list\n");
397  sprint(caller, " List available minigames\n");
398  sprint(caller, "Usage:^3 cmd minigame list-sessions\n");
399  sprint(caller, " List available minigames sessions\n");
400  sprint(caller, "Usage:^3 cmd minigame part|end\n");
401  sprint(caller, " Leave the current minigame\n");
402  sprint(caller, "Usage:^3 cmd minigame invite <player>\n");
403  sprint(caller, " Invite the given player to join you in a minigame\n");
404 }
void minigame_resend(entity minigame)
int int int imp
Definition: impulse.qc:90
const int MINIG_SF_ALL
Definition: minigames.qh:112
float MOVETYPE_WALK
Definition: progsdefs.qc:249
entity GetIndexedEntity(int argc, float start_index)
Definition: common.qc:83
entity join_minigame(entity player, string game_id)
Join an existing minigame session.
int team
Definition: main.qh:157
entity() spawn
ClientState CS(Client this)
Definition: state.qh:47
void part_minigame(entity player)
#define IS_OBSERVER(v)
Definition: utils.qh:11
int msle_id(string class_name)
Definition: minigames.qc:99
float VerifyClientEntity(entity client, float must_be_real, float must_be_bots)
Definition: common.qc:47
string netname
Definition: powerups.qc:20
entity to
Definition: self.qh:96
string classname
Definition: csprogsdefs.qc:107
int minigame_flags
Definition: minigames.qh:103
const int MINIG_SF_UPDATE
Definition: minigames.qh:109
entity owner
Definition: main.qh:73
bool autocvar_sv_minigames_observer
Definition: sv_minigames.qh:4
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
#define argv_end_index
Definition: dpextensions.qh:30
Force the player to spectator team.
Definition: teamplay.qh:137
void ClientCommand_minigame(entity caller, int request, int argc, string command)
#define MINIGAME_SIMPLELINKED_ENTITIES
How to create a minigame
Definition: all.qh:100
bool MinigameImpulse(entity this, int imp)
bool minigame_CheckSend(entity this, entity client)
entity active_minigame
Definition: cl_minigames.qh:85
void minigame_rmplayer(entity minigame_session, entity player)
Definition: sv_minigames.qc:17
entity minigame_players
For minigame sessions: list of players For minigame_player: client entity.
Definition: sv_minigames.qh:45
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
entity minigame_get_descriptor(string id)
Definition: minigames.qc:5
void player_clear_minigame(entity player)
Definition: sv_minigames.qc:6
void Player_SetForcedTeamIndex(entity player, int team_index)
Sets the index of the forced team of the given player.
Definition: teamplay.qc:351
float MOVETYPE_FLY_WORLDONLY
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
Definition: client.qc:238
#define NULL
Definition: post.qh:17
const int CMD_REQUEST_COMMAND
Definition: command.qh:3
bool autocvar_sv_minigames
Definition: sv_minigames.qh:3
void GameLogEcho(string s)
Definition: gamelog.qc:12
#define tokenize_console
Definition: dpextensions.qh:24
string invite_minigame(entity inviter, entity player)
Invite a player to join in a minigame.
const int MINIG_SF_CREATE
Definition: minigames.qh:108
void end_minigames()
Don&#39;t force any team.
Definition: teamplay.qh:138
#define LOG_TRACE(...)
Definition: log.qh:81
int minigame_addplayer(entity minigame_session, entity player)
entity start_minigame(entity player, string minigame)
Create a new minigame session.
#define strfree(this)
Definition: string.qh:56
bool minigame_SendEntity(entity this, entity to, int sf)
Definition: sv_minigames.qc:64
entity minigame_sessions
Definition: sv_minigames.qh:47
entity descriptor
For minigame sessions: minigame descriptor object.
Definition: minigames.qh:45
entity minigame_find_player(entity client)
#define FOREACH(list, cond, body)
Definition: iter.qh:19
void set_movetype(entity this, int mt)
#define IS_PLAYER(v)
Definition: utils.qh:9
void end_minigame(entity minigame_session)