1 #ifdef PAWN
2
3 #include <SDL_timer.h>
4
5 #include "elpawn.h"
6 #include "amx.h"
7 #include "amxaux.h"
8
9 #include "../errors.h"
10 #include "../events.h"
11
12 // includes for our native functions
13 #include "amxcons.h"
14 #include "amxfloat.h"
15 #include "amxstring.h"
16 #include "amxel.h"
17
18 // Set up our own struct with the information we need. We might be able
19 // to divine this from the AMX structure itself, but this is easier.
20 typedef struct
21 {
22 int initialized; // 1 if the machine was succesfully initialized
23 size_t buf_size; // size of the memory buffer
24 void *buffer; // memory buffer for the machine
25 AMX amx; // representation of the actual machine
26 } pawn_machine;
27
28 typedef struct _pawn_timer_queue
29 {
30 Uint32 ticks;
31 char* function;
32 Uint32 interval;
33 struct _pawn_timer_queue *next;
34 } pawn_timer_queue;
35
36 // the machines themselves
37 static pawn_machine srv_amx = {0};
38 static pawn_machine map_amx = {0};
39
40 static pawn_timer_queue *map_timer_queue = NULL;
41
ticks_less(Uint32 ticks,Uint32 limit)42 static __inline__ int ticks_less (Uint32 ticks, Uint32 limit)
43 {
44 // try to adjust for timer wrap around. We'll assume that no events are
45 // planned more than 2*31 ms (a bit more than 24 days) in advance.
46 return ticks < limit || (((Sint32) limit) >= 0 && ((Sint32) ticks) < 0);
47 }
48
ticks_less_equal(Uint32 ticks,Uint32 limit)49 static __inline__ int ticks_less_equal (Uint32 ticks, Uint32 limit)
50 {
51 return !ticks_less (limit, ticks);
52 }
53
54 // static __inline__ int ticks_greater_equal(Uint32 ticks, Uint32 limit)
55 // {
56 // return !ticks_less (ticks, limit);
57 // }
58
pop_timer_queue(pawn_timer_queue ** queue)59 void pop_timer_queue (pawn_timer_queue **queue)
60 {
61 if (*queue)
62 {
63 pawn_timer_queue *head = *queue;
64 *queue = head->next;
65 free (head->function);
66 free (head);
67 }
68 }
69
insert_timer_queue_node(pawn_timer_queue ** queue,pawn_timer_queue * new_node)70 void insert_timer_queue_node (pawn_timer_queue **queue, pawn_timer_queue *new_node)
71 {
72
73 if (*queue == NULL || ticks_less_equal (new_node->ticks, (*queue)->ticks))
74 {
75 new_node->next = *queue;
76 *queue = new_node;
77 }
78 else
79 {
80 pawn_timer_queue* node = *queue;
81 while (node->next && ticks_less (node->next->ticks, new_node->ticks))
82 node = node->next;
83 new_node->next = node->next;
84 node->next = new_node;
85 }
86 }
87
reschedule_timer_queue_head(pawn_timer_queue ** queue,Uint32 diff)88 void reschedule_timer_queue_head (pawn_timer_queue **queue, Uint32 diff)
89 {
90 pawn_timer_queue *head = *queue;
91
92 if (head)
93 {
94 head->ticks += diff;
95 if (head->next && ticks_less (head->next->ticks, head->ticks))
96 {
97 *queue = head->next;
98 insert_timer_queue_node (queue, head);
99 }
100 }
101 }
102
push_timer_queue(pawn_timer_queue ** queue,Uint32 ticks,const char * function,Uint32 interval)103 void push_timer_queue (pawn_timer_queue **queue, Uint32 ticks, const char* function, Uint32 interval)
104 {
105 pawn_timer_queue* new_node = calloc (1, sizeof (pawn_timer_queue));
106 new_node->ticks = ticks;
107 new_node->function = strdup (function);
108 new_node->interval = interval;
109 insert_timer_queue_node (queue, new_node);
110 }
111
initialize_pawn_machine(pawn_machine * machine,const char * fname)112 int initialize_pawn_machine (pawn_machine *machine, const char* fname)
113 {
114 int err;
115 size_t memsize;
116 void* buffer;
117
118 machine->initialized = 0;
119
120 memsize = aux_ProgramSize (fname);
121 if (memsize == 0)
122 {
123 LOG_ERROR ("Unable to determine memory size for Pawn file %s", fname);
124 return 0;
125 }
126
127 buffer = malloc (memsize);
128 if (buffer == NULL)
129 {
130 LOG_ERROR ("unable to allocate memory for Pawn file %s", fname);
131 return 0;
132 }
133
134 err = aux_LoadProgram (&(machine->amx), fname, buffer);
135 if (err != AMX_ERR_NONE)
136 {
137 free (buffer);
138 LOG_ERROR ("unable to load Pawn file %s", fname);
139 return 0;
140 }
141
142 // The amx_*Init functions return an error when not all natives
143 // used in the amx script are defined, so we only need to check
144 // the last Init (when we have defined our entire library).
145 amx_ConsoleInit (&(machine->amx));
146 amx_FloatInit (&(machine->amx));
147 amx_StringInit (&(machine->amx));
148 err = amx_ElInit (&(machine->amx));
149 if (err != AMX_ERR_NONE)
150 {
151 free (buffer);
152 LOG_ERROR ("Unable to initialize all native functions for Pawn file %s", fname);
153 return 0;
154 }
155
156 machine->buf_size = memsize;
157 machine->buffer = buffer;
158 machine->initialized = 1;
159
160 return 1;
161 }
162
initialize_pawn()163 int initialize_pawn ()
164 {
165 int srv_ok = initialize_pawn_machine (&srv_amx, "pawn_scripts/pawn_test.amx");
166 int map_ok = initialize_pawn_machine (&map_amx, "pawn_scripts/pawn_test.amx");
167 return srv_ok && map_ok;
168 }
169
cleanup_pawn_machine(pawn_machine * machine)170 void cleanup_pawn_machine (pawn_machine *machine)
171 {
172 if (machine->initialized)
173 {
174 amx_ElCleanup (&(machine->amx));
175 amx_StringCleanup (&(machine->amx));
176 amx_FloatCleanup (&(machine->amx));
177 amx_ConsoleCleanup (&(machine->amx));
178 amx_Cleanup (&(machine->amx));
179 machine->initialized = 0;
180 }
181
182 if (machine->buffer)
183 {
184 free (machine->buffer);
185 machine->buffer = NULL;
186 machine->buf_size = 0;
187 }
188 }
189
cleanup_pawn()190 void cleanup_pawn ()
191 {
192 cleanup_pawn_machine (&srv_amx);
193 cleanup_pawn_machine (&map_amx);
194 }
195
run_pawn_function(pawn_machine * machine,const char * fun,const char * fmt,va_list ap)196 int run_pawn_function (pawn_machine *machine, const char* fun, const char* fmt, va_list ap)
197 {
198 int err = AMX_ERR_NONE, index=-1;
199 int nr_ref_args = 0, ref_args_size = 0;
200 cell *ref_args = NULL;
201 int nr_args;
202
203 if (!machine->initialized)
204 {
205 LOG_ERROR ("Unable to execute Pawn function: machine not initialized");
206 return 0;
207 }
208
209 err = amx_FindPublic (&(machine->amx), fun, &index);
210 if (err != AMX_ERR_NONE)
211 {
212 LOG_ERROR ("Unable to locate Pawn function %s", fun);
213 return 0;
214 }
215
216 if (fmt != NULL && (nr_args = strlen (fmt)) > 0)
217 {
218 const char *s;
219 int i;
220 REAL f;
221 cell *phys;
222 int iarg;
223 cell *args = calloc (nr_args, sizeof (cell));
224
225 // first store the arguments in the array
226 for (iarg = 0; iarg < nr_args; iarg++)
227 {
228 switch (fmt[iarg])
229 {
230 case 'i':
231 i = va_arg (ap, int);
232 args[iarg] = (cell) i;
233 break;
234 case 'f':
235 f = va_arg (ap, REAL);
236 args[iarg] = *((cell*) &f);
237 break;
238 case 's':
239 s = va_arg (ap, const char*);
240 args[iarg] = (cell) s;
241 break;
242 default:
243 LOG_ERROR ("unknown format specifier '%c' in Pawn call", fmt[iarg]);
244 free (args);
245 return 1;
246 }
247 }
248
249 // now push the arguments to Pawn, in reverse order
250 for (iarg = nr_args-1; iarg >= 0; iarg--)
251 {
252 if (fmt[iarg] == 'i' || fmt[iarg] == 'f')
253 {
254 amx_Push (&(machine->amx), args[iarg]);
255 }
256 else if (fmt[iarg] == 's')
257 {
258 if (nr_ref_args >= ref_args_size)
259 {
260 ref_args_size += 8;
261 ref_args = realloc (ref_args, ref_args_size * sizeof (cell));
262 }
263 amx_PushString (&(machine->amx), ref_args+nr_ref_args, &phys, (const char *)args[iarg], 0, 0);
264 nr_ref_args++;
265 }
266 }
267
268 // we're done with these
269 free (args);
270 }
271
272 err = amx_Exec (&(machine->amx), NULL, index);
273 if (err != AMX_ERR_NONE)
274 {
275 LOG_ERROR ("Error %d executing Pawn function %s", err, fun);
276 return 0;
277 }
278
279 if (ref_args)
280 {
281 int i;
282 for (i = 0; i < nr_ref_args; i++)
283 amx_Release (&(machine->amx), ref_args[i]);
284 free (ref_args);
285 }
286
287 return 1;
288 }
289
run_pawn_server_function(const char * fun,const char * fmt,...)290 int run_pawn_server_function (const char *fun, const char* fmt, ...)
291 {
292 int res;
293 va_list ap;
294
295 va_start (ap, fmt);
296 res = run_pawn_function (&srv_amx, fun, fmt, ap);
297 va_end (ap);
298
299 return res;
300 }
301
run_pawn_map_function(const char * fun,const char * fmt,...)302 int run_pawn_map_function (const char* fun, const char* fmt, ...)
303 {
304 int res;
305 va_list ap;
306
307 va_start (ap, fmt);
308 res = run_pawn_function (&map_amx, fun, fmt, ap);
309 va_end (ap);
310
311 return res;
312 }
313
check_pawn_timers()314 void check_pawn_timers ()
315 {
316 if (map_timer_queue && ticks_less_equal (map_timer_queue->ticks, SDL_GetTicks ()))
317 {
318 SDL_Event event;
319 event.type = SDL_USEREVENT;
320 event.user.code = EVENT_PAWN_TIMER;
321 SDL_PushEvent (&event);
322 }
323 }
324
handle_pawn_timers()325 void handle_pawn_timers ()
326 {
327 Uint32 now = SDL_GetTicks ();
328 while (map_timer_queue && ticks_less_equal (map_timer_queue->ticks, now))
329 {
330 int ok = run_pawn_map_function (map_timer_queue->function, NULL);
331 if (ok && map_timer_queue->interval)
332 reschedule_timer_queue_head (&map_timer_queue, map_timer_queue->interval);
333 else
334 pop_timer_queue (&map_timer_queue);
335 }
336 }
337
add_map_timer(Uint32 offset,const char * name,Uint32 interval)338 void add_map_timer (Uint32 offset, const char* name, Uint32 interval)
339 {
340 push_timer_queue (&map_timer_queue, SDL_GetTicks () + offset, name, interval);
341 }
342
clear_map_timers()343 void clear_map_timers ()
344 {
345 while (map_timer_queue)
346 pop_timer_queue (&map_timer_queue);
347 }
348
349 #endif // PAWN
350