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