Introducing the GIB Object System (TM). This breaks scriptable HUD for now

and probably has enough bugs to leave the Orkin man scratching his head,
but it works and allows you to do neat things like write classes in GIB
(amazing!) and subclass builtin classes (which are Object and Thread at the
moment, Hash should be coming soon as a replacement for stem and leaf
variables).
This commit is contained in:
Brian Koropoff 2003-09-11 06:03:13 +00:00
parent f28cc3e8ec
commit 687a0845b6
36 changed files with 1402 additions and 489 deletions

View file

@ -4,8 +4,8 @@ SUBDIRS = QF
EXTRA_DIST = asm_i386.h alsa_funcs_list.h adivtab.h anorm_dots.h anorms.h \
asm_draw.h block16.h block8.h buildnum.h compat.h context_sdl.h \
context_x11.h d_iface.h d_ifacea.h d_local.h dga_check.h exp.h fbset.h \
getopt.h gib_buffer.h gib_builtin.h gib_execute.h gib_function.h \
gib_handle.h gib_parse.h gib_process.h gib_regex.h gib_semantics.h \
getopt.h gib_buffer.h gib_builtin.h gib_classes.h gib_execute.h gib_function.h \
gib_handle.h gib_object.h gib_parse.h gib_process.h gib_regex.h gib_semantics.h \
gib_thread.h gib_tree.h gib_vars.h gl_warp_sin.h in_win.h logos.h menu.h \
net_dgrm.h net_loop.h net_udp.h net_vcr.h net_wins.h netchan.h netmain.h \
old_keys.h ops.h qstring.h quakeasm.h regex.h r_cvar.h r_dynamic.h \

View file

@ -54,6 +54,7 @@ typedef struct cbuf_s {
enum {
CBUF_STATE_NORMAL = 0, // Normal condition
CBUF_STATE_WAIT, // Buffer is stalled until next frame
CBUF_STATE_BLOCKED, // Buffer is blocked until further notice
CBUF_STATE_ERROR, // An unrecoverable error occured
CBUF_STATE_STACK, // A buffer has been added to the stack
CBUF_STATE_JUNK // Buffer can be freed or reused

View file

@ -36,6 +36,96 @@
#include "QF/dstring.h"
#include "QF/cbuf.h"
#include "QF/hash.h"
#include "QF/llist.h"
// Object interface
typedef void (*gib_reply_handler) (int argc, const char **argv, void *data);
typedef struct gib_signal_s {
const char *name;
struct gib_object_s *receiver;
struct gib_slot_s *slot;
} gib_signal_t;
typedef struct gib_slot_s {
const char *mesg;
struct gib_object_s *sender;
struct gib_signal_s *signal;
} gib_slot_t;
typedef struct gib_object_s {
struct gib_class_s *class;
hashtab_t *methods;
void **data;
unsigned long int handle, refs;
hashtab_t *signals;
llist_t *slots;
} gib_object_t;
typedef struct gib_message_s {
int argc;
const char **argv;
gib_reply_handler reply;
void *replydata;
} gib_message_t;
typedef struct gib_method_s {
const char *name;
int (*func) (struct gib_object_s *obj, struct gib_method_s *method, void *data, gib_message_t message);
struct gib_method_s *parent;
struct gib_class_s *class;
void *data;
} gib_method_t;
typedef int (*gib_message_handler) (gib_object_t *obj, gib_method_t *method, void *data, gib_message_t message);
typedef void * (*gib_obj_constructor) (gib_object_t *obj);
typedef void (*gib_obj_destructor) (void *data);
typedef struct gib_class_s {
const char *name;
hashtab_t *methods, *class_methods;
gib_obj_constructor construct, class_construct;
gib_obj_destructor destruct;
unsigned int depth;
struct gib_object_s *classobj;
struct gib_class_s *parent;
} gib_class_t;
typedef struct gib_methodtab_s {
const char *name;
gib_message_handler func;
void *data;
} gib_methodtab_t;
typedef struct gib_classdesc_s {
const char *name;
const char *parentname;
gib_obj_constructor construct, class_construct;
gib_obj_destructor destruct;
struct gib_methodtab_s *methods, *class_methods;
} gib_classdesc_t;
#define GIB_Reply(mesg,argc,argv) if ((mesg).reply) ((mesg).reply(argc,argv,(mesg).replydata))
#define GIB_ForwardToSuper(mesg,obj,method) ((method)->parent->func ((obj), (method)->parent, (obj)->data[(method)->parent->class->depth], (mesg)))
void GIB_Class_Create (gib_classdesc_t *desc);
gib_object_t *GIB_Object_Create (const char *classname, qboolean classobj);
void GIB_Object_Destroy (gib_object_t *obj);
void GIB_Object_Incref (gib_object_t *obj);
void GIB_Object_Decref (gib_object_t *obj);
int GIB_Send (gib_object_t *obj, int argc, const char **argv, gib_reply_handler reply, void *replydata);
int GIB_SendToMethod (gib_object_t *obj, gib_method_t *method, int argc, const
char **argv, gib_reply_handler reply, void *replydata);
gib_object_t *GIB_Object_Get (const char *id);
void GIB_Object_Signal_Slot_Pair (gib_object_t *sender, const char *signal,
gib_object_t *receiver, const char *slot);
void GIB_Object_Signal_Slot_Destroy (gib_object_t *sender, const char *signal,
gib_object_t *receiver, const char *slot);
void GIB_Object_Signal_Emit (gib_object_t *sender, int argc, const char
**argv);
void GIB_Object_Init (void);
// Buffer access (required to use GIB_Arg* macros)
@ -58,8 +148,15 @@ typedef struct gib_buffer_data_s {
} *values;
unsigned int size, p;
} stack;
struct {
struct gib_object_s *obj;
struct gib_method_s *method;
struct gib_message_s mesg;
} reply;
struct hashtab_s *locals; // Local variables
struct hashtab_s *globals; // Current domain
void (*dnotify) (cbuf_t *cbuf, void *data);
void *ddata;
} gib_buffer_data_t;
// Builtin function interface
@ -99,6 +196,7 @@ cbuf_interpreter_t *GIB_Interpreter (void);
// Thread interface
void GIB_Thread_Execute (void);
unsigned int GIB_Thread_Count (void);
// Init interface
@ -106,9 +204,8 @@ void GIB_Init (qboolean sandbox);
// Handle interface
unsigned long int GIB_Handle_New (void *data, unsigned short int class);
void GIB_Handle_Free (unsigned long int num, unsigned short int class);
void *GIB_Handle_Get (unsigned long int num, unsigned short int class);
unsigned short int GIB_Handle_Class_New (void);
unsigned long int GIB_Handle_New (gib_object_t *data);
void GIB_Handle_Free (unsigned long int num);
gib_object_t *GIB_Handle_Get (unsigned long int num);
#endif

View file

@ -58,8 +58,9 @@ llist_node_t *llist_getnode (llist_t *list, void *element);
llist_node_t *llist_insertafter (llist_node_t *ref, void *element);
llist_node_t *llist_insertbefore (llist_node_t *ref, void *element);
void *llist_remove (llist_node_t *ref);
unsigned int llist_size (llist_t *llist);
void llist_iterate (llist_t *list, llist_iterator_t iterate);
void *llist_find (llist_t *list, void *comparison);
llist_node_t *llist_findnode (llist_t *list, void *comparison);
void *llist_createarray (llist_t *list, size_t esize);
#endif

View file

@ -40,6 +40,7 @@ void GIB_Buffer_Set_Program (cbuf_t *cbuf, gib_tree_t *program);
void GIB_Buffer_Push_Sstack (struct cbuf_s *cbuf);
void GIB_Buffer_Pop_Sstack (struct cbuf_s *cbuf);
dstring_t *GIB_Buffer_Dsarray_Get (struct cbuf_s *cbuf);
void GIB_Buffer_Reply_Callback (int argc, const char **argv, void *data);
void GIB_Buffer_Error (cbuf_t *cbuf, const char *type, const char *fmt, va_list args);
extern struct cbuf_interpreter_s gib_interp;

38
include/gib_classes.h Normal file
View file

@ -0,0 +1,38 @@
/*
gib_classes.h
Built-in GIB classes
Copyright (C) 2003 Brian Koropoff
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#ifndef _GIB_CLASSES_H
#define _GIB_CLASSES_H
#include "gib_tree.h"
void GIB_Classes_Build_Scripted (const char *name, const char *parentname,
gib_tree_t *tree, gib_script_t *script);
void GIB_Classes_Init (void);
#endif

View file

@ -46,7 +46,7 @@ typedef struct gib_function_s {
qboolean exported;
} gib_function_t;
void GIB_Function_Define (const char *name, const char *text, gib_tree_t *program, gib_script_t *script, hashtab_t *globals);
gib_function_t *GIB_Function_Define (const char *name, const char *text, gib_tree_t *program, gib_script_t *script, hashtab_t *globals);
gib_function_t *GIB_Function_Find (const char *name);
void GIB_Function_Prepare_Args (cbuf_t *cbuf, const char **args, unsigned int argc);
void GIB_Function_Prepare_Args_D (cbuf_t *cbuf, dstring_t **args, unsigned int argc);

View file

@ -26,6 +26,7 @@
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
@ -33,9 +34,8 @@
#define __gib_handle_h
typedef struct gib_handle_s {
unsigned short int class;
unsigned long int num;
void *data;
gib_object_t *data;
struct gib_handle_s *next;
} gib_handle_t;

36
include/gib_object.h Normal file
View file

@ -0,0 +1,36 @@
/*
gib_object.h
GIB object handling functions
Copyright (C) 2003 Brian Koropoff
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#ifndef _GIB_OBJECT_H
#define _GIB_OBJECT_H
#define CLASSDATA(class,type) ((type *) ((class)->classobj->data[(class)->depth]))
void GIB_Object_Init (void);
#endif

View file

@ -33,23 +33,13 @@
#define __gib_thread_h
#include "gib_function.h"
#include "QF/cbuf.h"
typedef struct gib_thread_s {
unsigned long int id;
struct cbuf_s *cbuf;
struct gib_thread_s *prev, *next;
} gib_thread_t;
void GIB_Thread_Add (gib_thread_t *thread);
void GIB_Thread_Remove (gib_thread_t *thread);
void GIB_Thread_Delete (gib_thread_t *thread);
gib_thread_t *GIB_Thread_New (void);
cbuf_t *GIB_Thread_New (void);
void GIB_Thread_Delete (cbuf_t *thread);
void GIB_Thread_Init (void);
int GIB_Event_Register (const char *name, gib_function_t *func);
void GIB_Event_Init (void);
extern gib_thread_t *gib_thread_first, *gib_thread_last;
extern unsigned short int gib_thread_class;
#endif

View file

@ -53,11 +53,12 @@ typedef struct gib_tree_s {
TREE_T_CMD, // A command
TREE_T_COND, // Conditional jump
TREE_T_ASSIGN, // Assignment
TREE_T_SEND, // Message sending
TREE_T_JUMP, // Jump
TREE_T_ARG, // Argument (not a line)
TREE_T_FORNEXT, // Fetch next arg in for loop
TREE_T_META, // Info node
TREE_T_NOP // Do nothing (label, etc)
TREE_T_LABEL // Label (or no-op)
} type;
struct gib_tree_s *children, *next, *jump;
} gib_tree_t;

View file

@ -53,5 +53,6 @@ gib_var_t *GIB_Var_Get_Complex (hashtab_t **first, hashtab_t **second, char *key
gib_var_t *GIB_Var_Get_Very_Complex (hashtab_t ** first, hashtab_t ** second, dstring_t *key, unsigned int start, unsigned int *ind, qboolean create);
void GIB_Var_Assign (gib_var_t *var, unsigned int index, dstring_t **values, unsigned int numv, qboolean shrink);
hashtab_t *GIB_Domain_Get (const char *name);
hashtab_t *GIB_Var_Hash_New (void);
void GIB_Var_Init (void);

View file

@ -146,39 +146,32 @@ bi_GIB_Return (progs_t *pr)
R_INT (pr) = GIB_CanReturn () ? 1 : 0;
}
static void
bi_GIB_Handle_Class_New (progs_t *pr)
{
R_INT (pr) = GIB_Handle_Class_New ();
}
static void
bi_GIB_Handle_New (progs_t *pr)
{
long *qcptr = malloc (sizeof (long));
*qcptr = P_POINTER (pr, 0);
R_INT (pr) = GIB_Handle_New (qcptr, P_INT (pr, 1));
//long *qcptr = malloc (sizeof (long));
//*qcptr = P_POINTER (pr, 0);
//R_INT (pr) = GIB_Handle_New (qcptr);
}
static void
bi_GIB_Handle_Free (progs_t *pr)
{
unsigned long int hand = P_INT (pr, 0);
unsigned short int cl = (unsigned short) P_INT (pr, 1);
long *qcptr = GIB_Handle_Get (hand, cl);
//unsigned long int hand = P_INT (pr, 0);
//long *qcptr = GIB_Handle_Get (hand);
free (qcptr);
GIB_Handle_Free (hand, cl);
//free (qcptr);
//GIB_Handle_Free (hand);
}
static void
bi_GIB_Handle_Get (progs_t *pr)
{
long *hand = GIB_Handle_Get (P_INT (pr, 0), (unsigned short) P_INT (pr, 1));
if (hand)
R_INT (pr) = *hand;
else
R_INT (pr) = 0;
//long *hand = GIB_Handle_Get (P_INT (pr, 0));
//if (hand)
// R_INT (pr) = *hand;
//else
// R_INT (pr) = 0;
}
void
@ -193,7 +186,6 @@ GIB_Progs_Init (progs_t *pr)
PR_AddBuiltin (pr, "GIB_Builtin_Add", bi_GIB_Builtin_Add, -1);
PR_AddBuiltin (pr, "GIB_Return", bi_GIB_Return, -1);
PR_AddBuiltin (pr, "GIB_Handle_Class_New", bi_GIB_Handle_Class_New, -1);
PR_AddBuiltin (pr, "GIB_Handle_New", bi_GIB_Handle_New, -1);
PR_AddBuiltin (pr, "GIB_Handle_Free", bi_GIB_Handle_Free, -1);
PR_AddBuiltin (pr, "GIB_Handle_Get", bi_GIB_Handle_Get, -1);

View file

@ -6,6 +6,6 @@ lib_LTLIBRARIES= libQFgib.la
libQFgib_la_LDFLAGS= -version-info 1:0:0
libQFgib_la_SOURCES= \
gib_buffer.c gib_builtin.c gib_execute.c gib_function.c gib_parse.c gib_handle.c \
gib_process.c gib_regex.c gib_thread.c gib_vars.c gib_init.c gib_tree.c \
gib_buffer.c gib_builtin.c gib_classes.c gib_execute.c gib_function.c gib_parse.c gib_handle.c \
gib_object.c gib_process.c gib_regex.c gib_thread.c gib_vars.c gib_init.c gib_tree.c \
gib_semantics.c ops.c exp.c regex.c

View file

@ -67,6 +67,9 @@ GIB_Buffer_Destruct (struct cbuf_s *cbuf)
gib_buffer_data_t *g = GIB_DATA (cbuf);
unsigned int i, j;
if (g->dnotify)
g->dnotify (cbuf, g->ddata);
dstring_delete (g->arg_composite);
if (g->locals)
Hash_DelTable (g->locals);
@ -107,7 +110,7 @@ GIB_Buffer_Reset (struct cbuf_s *cbuf)
g->program = g->ip = 0;
g->stack.p = 0;
g->waitret = false;
g->reply.obj = NULL;
}
void
@ -265,6 +268,18 @@ GIB_Buffer_Get_Line_Info (cbuf_t * cbuf, char **line)
}
}
void
GIB_Buffer_Reply_Callback (int argc, const char **argv, void *data)
{
cbuf_t *cbuf = (cbuf_t *) data;
int i;
for (i = 0; i < argc; i++)
dstring_copystr (GIB_Buffer_Dsarray_Get (cbuf), argv[i]);
if (cbuf->state == CBUF_STATE_BLOCKED)
cbuf->state = CBUF_STATE_NORMAL;
}
void
GIB_Buffer_Error (cbuf_t * cbuf, const char *type, const char *fmt,
va_list args)

View file

@ -64,6 +64,7 @@ const char rcsid[] =
#include "gib_thread.h"
#include "gib_handle.h"
#include "gib_builtin.h"
#include "gib_classes.h"
char gib_null_string[] = "";
@ -235,7 +236,7 @@ GIB_Local_f (void)
static void
GIB_Global_f (void)
GIB_Shared_f (void)
{
gib_var_t *var;
unsigned int index;
@ -312,7 +313,19 @@ GIB_Return_f (void)
GIB_DATA (cbuf_active)->ip = &fakeip;
if (GIB_Argc () > 1 && sp && sp->interpreter == &gib_interp
if (GIB_DATA (cbuf_active)->reply.obj) {
gib_buffer_data_t *g = GIB_DATA (cbuf_active);
const char **argv = malloc (sizeof (char *) * GIB_Argc() -1);
int i;
for (i = 1; i < GIB_Argc(); i++)
argv[i-1] = GIB_Argv(i);
GIB_Reply (g->reply.mesg, GIB_Argc()-1, argv);
free (argv);
g->dnotify = NULL;
GIB_Object_Decref (g->reply.obj);
} else if (GIB_Argc () > 1 && sp && sp->interpreter == &gib_interp
&& GIB_DATA (sp)->waitret) {
int i;
dstring_t *dstr;
@ -714,68 +727,6 @@ GIB_Text_From_Decimal_f (void)
}
}
static void
GIB_Thread_Create_f (void)
{
gib_function_t *f;
if (GIB_Argc () < 2)
GIB_USAGE ("function [arg1 arg2 ...]");
else if (!(f = GIB_Function_Find (GIB_Argv (1))))
GIB_Error ("function", "%s: no function named '%s' exists.",
GIB_Argv (0), GIB_Argv (1));
else {
gib_thread_t *thread = GIB_Thread_New ();
GIB_Function_Execute_D (thread->cbuf, f, cbuf_active->args->argv + 1,
cbuf_active->args->argc - 1);
GIB_Thread_Add (thread);
if (GIB_CanReturn ())
dsprintf (GIB_Return (0), "%lu", thread->id);
}
}
static void
GIB_Thread_Kill_f (void)
{
if (GIB_Argc () != 2)
GIB_USAGE ("id");
else {
gib_thread_t *thread;
cbuf_t *cur;
unsigned long int id = strtoul (GIB_Argv (1), 0, 10);
thread = GIB_Handle_Get (id, gib_thread_class);
if (!thread) {
GIB_Error ("thread", "%s: thread %lu does not exist.", GIB_Argv (0), id);
return;
}
// If we are currently running this thread, set an error state so we exit it cleanly
// if it were simply nuked, a crash would result
for (cur = thread->cbuf; cur->down && cur->down->state != CBUF_STATE_JUNK; cur = cur->down)
if (cur == cbuf_active) {
cur->state = CBUF_STATE_ERROR;
return;
}
GIB_Thread_Remove (thread);
GIB_Thread_Delete (thread);
}
}
static void
GIB_Thread_List_f (void)
{
if (GIB_Argc () != 1)
GIB_USAGE ("");
else if (GIB_CanReturn ()) {
gib_thread_t *cur;
for (cur = gib_thread_first; cur; cur = cur->next)
dsprintf (GIB_Return (0), "%lu", cur->id);
}
}
static void
GIB_Event_Register_f (void)
{
@ -998,6 +949,42 @@ GIB_Print_f (void)
Sys_Printf ("%s", GIB_Argv (1));
}
static void
GIB_Class_f (void)
{
if (GIB_Argc () == 5)
GIB_Classes_Build_Scripted (GIB_Argv(1), GIB_Argv(3),
GIB_Argm (4)->children,
GIB_DATA(cbuf_active)->script);
else
GIB_Classes_Build_Scripted (GIB_Argv(1), NULL,
GIB_Argm (2)->children,
GIB_DATA(cbuf_active)->script);
}
static void
GIB_Emit_f (void)
{
if (GIB_Argc () < 2) {
GIB_USAGE ("signal [arg1 arg2 ...]");
return;
} else if (!GIB_DATA(cbuf_active)->reply.obj) {
GIB_Error ("emit", "Cannot emit signal in this context.");
return;
} else {
int i;
const char **argv = malloc (GIB_Argc () - 1);
for (i = 1; i < GIB_Argc (); i ++)
argv[i-1] = GIB_Argv (1);
GIB_Object_Signal_Emit (GIB_DATA(cbuf_active)->reply.obj,
GIB_Argc () - 1, argv);
free (argv);
}
}
static void
GIB_bp1_f (void)
{
@ -1031,7 +1018,8 @@ GIB_Builtin_Init (qboolean sandbox)
GIB_Builtin_Add ("function::get", GIB_Function_Get_f);
GIB_Builtin_Add ("function::export", GIB_Function_Export_f);
GIB_Builtin_Add ("local", GIB_Local_f);
GIB_Builtin_Add ("global", GIB_Global_f);
GIB_Builtin_Add ("shared", GIB_Shared_f);
GIB_Builtin_Add ("global", GIB_Shared_f);
GIB_Builtin_Add ("delete", GIB_Delete_f);
GIB_Builtin_Add ("domain", GIB_Domain_f);
GIB_Builtin_Add ("domain::clear", GIB_Domain_Clear_f);
@ -1052,9 +1040,6 @@ GIB_Builtin_Init (qboolean sandbox)
GIB_Builtin_Add ("text::toBrown", GIB_Text_Brown_f);
GIB_Builtin_Add ("text::toDecimal", GIB_Text_To_Decimal_f);
GIB_Builtin_Add ("text::fromDecimal", GIB_Text_From_Decimal_f);
GIB_Builtin_Add ("thread::create", GIB_Thread_Create_f);
GIB_Builtin_Add ("thread::kill", GIB_Thread_Kill_f);
GIB_Builtin_Add ("thread::getList", GIB_Thread_List_f);
GIB_Builtin_Add ("event::register", GIB_Event_Register_f);
GIB_Builtin_Add ("file::read", GIB_File_Read_f);
GIB_Builtin_Add ("file::write", GIB_File_Write_f);
@ -1063,6 +1048,8 @@ GIB_Builtin_Init (qboolean sandbox)
GIB_Builtin_Add ("file::delete", GIB_File_Delete_f);
GIB_Builtin_Add ("range", GIB_Range_f);
GIB_Builtin_Add ("print", GIB_Print_f);
GIB_Builtin_Add ("class", GIB_Class_f);
GIB_Builtin_Add ("emit", GIB_Emit_f);
GIB_Builtin_Add ("bp1", GIB_bp1_f);
GIB_Builtin_Add ("bp2", GIB_bp2_f);
GIB_Builtin_Add ("bp3", GIB_bp3_f);

461
libs/gib/gib_classes.c Normal file
View file

@ -0,0 +1,461 @@
/*
gib_classes.c
GIB built-in classes
Copyright (C) 2003 Brian Koropoff
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/gib.h"
#include "QF/va.h"
#include "QF/sys.h"
#include "QF/llist.h"
#include "gib_object.h"
#include "gib_thread.h"
#include "gib_classes.h"
#include "gib_tree.h"
#include "gib_vars.h"
/*
Begin Object class
The root of the GIB class hierarchy. Responsible for retain/release
reference counting, creating instances of an object, returning
an object handle, and reporting object type.
*/
typedef struct baseobj_s {
llist_t *receivers, *subscribed;
} baseobj_t;
static int
Object_Retain_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
const char *r;
GIB_Object_Incref (obj);
r = va ("%lu", obj->handle);
GIB_Reply (mesg, 1, &r);
return 0;
}
static int
Object_Release_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
GIB_Object_Decref (obj);
return 0;
}
static int
Object_Init_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
const char *r;
r = va ("%lu", obj->handle);
GIB_Reply (mesg, 1, &r);
return 0;
}
static int
Object_Class_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
GIB_Reply (mesg, 1, &obj->class->name);
return 0;
}
static int
Object_Dispose_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
static const char *disposed = "disposed";
GIB_Object_Signal_Emit (obj, 1, &disposed);
GIB_Reply (mesg, 0, NULL);
return 0;
}
static int
Object_SignalConnect_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
gib_object_t *other;
if (mesg.argc >= 4 && (other = GIB_Object_Get (mesg.argv[2])))
GIB_Object_Signal_Slot_Pair (obj, mesg.argv[1], other,
mesg.argv[3]);
GIB_Reply (mesg, 0, NULL);
return 0;
}
static int
Object_SignalDisconnect_f (gib_object_t *obj, gib_method_t *method, void
*data, gib_message_t mesg)
{
gib_object_t *other;
if (mesg.argc >= 4 && (other = GIB_Object_Get (mesg.argv[2])))
GIB_Object_Signal_Slot_Destroy (obj, mesg.argv[1], other,
mesg.argv[3]);
GIB_Reply (mesg, 0, NULL);
return 0;
}
static int
Object_Class_New_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
const char *old;
gib_object_t *new;
new = GIB_Object_Create (obj->class->name, false);
old = mesg.argv[0];
mesg.argv[0] = "init";
GIB_Send (new, mesg.argc, mesg.argv, mesg.reply, mesg.replydata);
mesg.argv[0] = old;
return 0;
}
static void *
Object_Construct (gib_object_t *obj)
{
baseobj_t *base = malloc (sizeof (baseobj_t));
return base;
}
static void
Object_Destruct (void *data)
{
free (data);
return;
}
gib_methodtab_t Object_methods[] = {
{"retain", Object_Retain_f, NULL},
{"release", Object_Release_f, NULL},
{"init", Object_Init_f, NULL},
{"class", Object_Class_f, NULL},
{"dispose", Object_Dispose_f, NULL},
{"signalConnect", Object_SignalConnect_f, NULL},
{"signalDisconnect", Object_SignalDisconnect_f, NULL},
{NULL, NULL, NULL}
};
gib_methodtab_t Object_class_methods[] = {
{"new", Object_Class_New_f, NULL},
{"signalConnect", Object_SignalConnect_f, NULL},
{"signalDisconnect", Object_SignalDisconnect_f, NULL},
{NULL, NULL, NULL}
};
gib_classdesc_t Object_class = {
"Object", NULL,
Object_Construct, NULL,
Object_Destruct,
Object_methods, Object_class_methods
};
/* End Object class */
/*
Begin Thread class
Creates a front end to a separate GIB buffer that is executed every frame.
The object is initialized with a GIB function and set of arguments to that
function that gets executed immediately. When the thread exits, the object
gets sent a release message. Causing the object to be destroyed before the
thread exits will immediately terminate the thread. The object can be
retained so that it is not destroyed when the thread exits on its own; in
this case, the object must be released manually later on, but otherwise
will do nothing.
*/
typedef struct Thread_class_s {
hashtab_t *methods;
} Thread_class_t;
typedef struct Thread_s {
gib_object_t *obj;
cbuf_t *thread;
qboolean ended;
} Thread_t;
static int
Thread_Init_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
Thread_t *t = (Thread_t *) data;
gib_function_t *f;
if (mesg.argc < 2 || !(f = GIB_Function_Find (mesg.argv[1]))) {
GIB_Reply (mesg, 0, NULL);
GIB_Object_Destroy (obj);
return 0;
} else {
GIB_Function_Execute (t->thread, f, mesg.argv+1, mesg.argc-1);
Cbuf_Execute_Stack (t->thread);
return GIB_ForwardToSuper (mesg, obj, method);
}
}
static void
Thread_Cbuf_Freed (cbuf_t *cbuf, void *data)
{
Thread_t *t = (Thread_t *) data;
static const char *release = "release";
t->thread = NULL;
t->ended = true;
GIB_Send (t->obj, 1, &release, NULL, NULL);
}
static void *
Thread_Construct (gib_object_t *obj)
{
Thread_t *t = malloc (sizeof (Thread_t));
t->thread = GIB_Thread_New ();
GIB_DATA (t->thread)->dnotify = Thread_Cbuf_Freed;
GIB_DATA (t->thread)->ddata = t;
t->obj = obj;
t->ended = false;
return t;
}
static void
Thread_Destruct (void *data)
{
Thread_t *t = (Thread_t *) data;
if (!t->ended) {
GIB_DATA (t->thread)->dnotify = NULL;
GIB_Thread_Delete (t->thread);
}
free (t);
}
gib_methodtab_t Thread_methods[] = {
{"init", Thread_Init_f, NULL},
{NULL, NULL, NULL}
};
gib_methodtab_t Thread_class_methods[] = {
{NULL, NULL, NULL}
};
gib_classdesc_t Thread_class = {
"Thread", "Object",
Thread_Construct, NULL,
Thread_Destruct,
Thread_methods, Thread_class_methods
};
/*
Scripted object class factory
These functions and structs are used to create a class that executes
GIB functions in a new thread in response to messages. This allows classes
to be created in GIB itself.
*/
typedef struct Scrobj_s {
hashtab_t *shared;
} Scrobj_t;
typedef struct Scrobj_method_s {
gib_function_t *func;
} Scrobj_method_t;
static void *
Scrobj_Construct (gib_object_t *obj)
{
Scrobj_t *new = malloc (sizeof (Scrobj_t));
new->shared = GIB_Var_Hash_New ();
return new;
}
static void *
Scrobj_Class_Construct (gib_object_t *obj)
{
Scrobj_t *new = malloc (sizeof (Scrobj_t));
new->shared = GIB_Domain_Get (obj->class->name);
return new;
}
static void
Scrobj_Destruct (void *data)
{
Scrobj_t *s = (Scrobj_t *) data;
Hash_DelTable (s->shared);
free (s);
}
static void
Scrobj_Thread_Died (cbuf_t *thread, void *data)
{
GIB_Reply (GIB_DATA(thread)->reply.mesg, 0, NULL);
GIB_Object_Decref (GIB_DATA(thread)->reply.obj);
}
static int
Scrobj_Method_f (gib_object_t *obj, gib_method_t *method, void *data,
gib_message_t mesg)
{
static char this[] = "this";
unsigned int ind;
cbuf_t *thread = GIB_Thread_New ();
static hashtab_t *nhash = NULL;
gib_var_t *var;
GIB_DATA(thread)->dnotify = Scrobj_Thread_Died;
GIB_DATA(thread)->reply.obj = obj;
GIB_DATA(thread)->reply.method = method;
GIB_DATA(thread)->reply.mesg = mesg;
GIB_Function_Execute (thread, ((Scrobj_method_t *)method->data)->func,
mesg.argv, mesg.argc);
GIB_DATA(thread)->globals = ((Scrobj_t *)data)->shared;
var = GIB_Var_Get_Complex (&GIB_DATA(thread)->locals, &nhash, this,
&ind, true);
if (obj->handle)
dsprintf (var->array[0].value, "%lu", obj->handle);
else
dstring_copystr (var->array[0].value, obj->class->name);
GIB_Object_Incref (obj);
Cbuf_Execute_Stack (thread);
return 0;
}
void
GIB_Classes_Build_Scripted (const char *name, const char *parentname,
gib_tree_t *tree, gib_script_t *script)
{
gib_tree_t *line;
llist_t *methods, *cmethods;
gib_methodtab_t *mtab, *cmtab;
gib_classdesc_t desc;
enum {CLASS, INSTANCE} mode = INSTANCE;
static void
mtabfree (void *mtab, void *unused)
{
free (mtab);
}
static const char *
fname (const char *str)
{
if (mode == INSTANCE)
return va ("__%s_%s__", name, str);
else
return va ("%s::%s", name, str);
}
methods = llist_new (mtabfree, NULL, NULL);
cmethods = llist_new (mtabfree, NULL, NULL);
for (line = tree; line; line = line->next)
switch (line->type) {
case TREE_T_LABEL:
if (!strcmp (line->str, "class"))
mode = CLASS;
else if (!strcmp (line->str, "instance"))
mode = INSTANCE;
break;
case TREE_T_CMD:
if (!strcmp (line->children->str,
"function")) {
gib_methodtab_t *new = malloc (sizeof
(gib_methodtab_t));
Scrobj_method_t *data = malloc (sizeof
(Scrobj_method_t));
data->func = GIB_Function_Define
(fname
(line->children->next->str),
line->children->next->next->str,
line->children->next->next->children,
script, NULL);
new->data = data;
new->name = line->children->next->str;
new->func = Scrobj_Method_f;
if (mode == INSTANCE)
llist_append (methods, new);
else
llist_append (cmethods, new);
}
break;
default:
break;
}
llist_append (methods, calloc (1, sizeof (gib_methodtab_t)));
llist_append (cmethods, calloc (1, sizeof (gib_methodtab_t)));
mtab = llist_createarray (methods, sizeof (gib_methodtab_t));
cmtab = llist_createarray (cmethods, sizeof (gib_methodtab_t));
desc.name = name;
desc.parentname = parentname;
desc.construct = Scrobj_Construct;
desc.class_construct = Scrobj_Class_Construct;
desc.destruct = Scrobj_Destruct;
desc.methods = mtab;
desc.class_methods = cmtab;
GIB_Class_Create (&desc);
free (mtab);
free (cmtab);
llist_delete (methods);
llist_delete (cmethods);
}
void
GIB_Classes_Init (void)
{
GIB_Class_Create (&Object_class);
GIB_Class_Create (&Thread_class);
}

View file

@ -202,16 +202,21 @@ GIB_Execute (cbuf_t * cbuf)
gib_buffer_data_t *g = GIB_DATA (cbuf);
gib_builtin_t *b;
gib_function_t *f;
gib_object_t *obj;
unsigned int index;
gib_var_t *var;
int i;
qboolean super;
static const char **mesg = NULL;
static int maxmesg = 0;
if (!g->program)
return;
g->ip = g->ip ? g->ip->next : g->program;
while (g->ip) {
switch (g->ip->type) {
case TREE_T_NOP: // Move to next instruction
case TREE_T_LABEL: // Move to next instruction
g->ip = g->ip->next;
continue;
case TREE_T_JUMP: // Absolute jump
@ -244,12 +249,80 @@ GIB_Execute (cbuf_t * cbuf)
if (g->ip->flags & TREE_L_EMBED) {
GIB_Buffer_Push_Sstack (cbuf);
g->waitret = true;
if (GIB_CanReturn ())
for (i = 2; i < cbuf->args->argc; i++)
GIB_Return (cbuf->args->argv[i]->str);
} else
g->waitret = false;
g->ip = g->ip->next;
continue;
case TREE_T_SEND: // Message sending
if (GIB_Execute_Prepare_Line (cbuf, g->ip))
return;
if (cbuf->args->argc - 2 > maxmesg) {
maxmesg += 32;
mesg = realloc (mesg, sizeof (char *) * maxmesg);
}
for (i = 2; i < cbuf->args->argc; i++)
mesg[i-2] = cbuf->args->argv[i]->str;
super = false;
if (!strcmp (cbuf->args->argv[0]->str,
"super")) {
if (!(obj = g->reply.obj)) {
GIB_Error (
"send",
"Sending "
"message to "
"super not "
"possible in "
"this context."
);
return;
}
super = true;
} else if (!(obj = GIB_Object_Get (cbuf->args->argv[0]->str))) {
GIB_Error (
"send",
"No such object or class: %s",
cbuf->args->argv[0]->str
);
return;
}
if (g->ip->flags & TREE_L_EMBED) {
// Get ready for return values
g->waitret = true;
GIB_Buffer_Push_Sstack (cbuf);
cbuf->state = CBUF_STATE_BLOCKED;
i = super ? GIB_SendToMethod (obj,
g->reply.method->parent,
i - 2, mesg,
GIB_Buffer_Reply_Callback,
cbuf) : GIB_Send (obj,
i - 2, mesg,
GIB_Buffer_Reply_Callback,
cbuf);
} else {
g->waitret = false;
i = super ? GIB_SendToMethod (obj,
g->reply.method->parent,
i - 2, mesg,
NULL, NULL) :
GIB_Send (obj, i - 2,
mesg, NULL, NULL);
}
if (i < 0) {
GIB_Error (
"send",
"Object %s (%s) could not handle message %s",
cbuf->args->argv[0]->str,
obj->class->name,
cbuf->args->argv[2]->str
);
return;
}
g->ip = g->ip->next;
continue;
case TREE_T_CMD: // Normal command
if (GIB_Execute_Prepare_Line (cbuf, g->ip))
return;
@ -278,9 +351,9 @@ GIB_Execute (cbuf_t * cbuf)
cbuf->args->argv[0]->str
);
}
}
if (cbuf->state)
return;
}
g->ip = g->ip->next;
continue;
default: // We should never get here

View file

@ -102,7 +102,7 @@ GIB_Function_Free (void *ele, void *ptr)
allocating one and adding it to the functions
hash if needed.
*/
void
gib_function_t *
GIB_Function_Define (const char *name, const char *text, gib_tree_t * program,
gib_script_t * script, hashtab_t * globals)
{
@ -132,6 +132,8 @@ GIB_Function_Define (const char *name, const char *text, gib_tree_t * program,
func->program = program;
func->globals = globals;
func->script = script;
return func;
}
/*

View file

@ -38,13 +38,13 @@
static __attribute__ ((unused)) const char rcsid[] =
"$Id$";
static unsigned long int gib_next_handle, gib_next_class;
static unsigned long int gib_next_handle;
static gib_handle_t *gib_unused_handles;
static gib_handle_t **gib_handles;
static unsigned long int gib_handles_size;
unsigned long int
GIB_Handle_New (void *data, unsigned short int class)
GIB_Handle_New (gib_object_t *data)
{
gib_handle_t *new;
if (gib_unused_handles) {
@ -60,17 +60,16 @@ GIB_Handle_New (void *data, unsigned short int class)
new->num = num;
}
new->data = data;
new->class = class;
gib_handles[new->num] = new;
return new->num;
}
void
GIB_Handle_Free (unsigned long int num, unsigned short int class)
GIB_Handle_Free (unsigned long int num)
{
gib_handle_t *hand;
if (num >= gib_next_handle || gib_handles[num]->class != class)
if (num >= gib_next_handle || !gib_handles[num])
return;
hand = gib_handles[num];
gib_handles[num] = 0;
@ -78,26 +77,19 @@ GIB_Handle_Free (unsigned long int num, unsigned short int class)
gib_unused_handles = hand;
}
void *
GIB_Handle_Get (unsigned long int num, unsigned short int class)
gib_object_t *
GIB_Handle_Get (unsigned long int num)
{
if (num >= gib_next_handle || !gib_handles[num] || gib_handles[num]->class != class)
if (num >= gib_next_handle || !gib_handles[num])
return 0;
return gib_handles[num]->data;
}
unsigned short int
GIB_Handle_Class_New (void)
{
return gib_next_class++;
}
void
GIB_Handle_Init (void)
{
gib_handles_size = 256;
gib_handles = calloc (gib_handles_size, sizeof (gib_handle_t *));
gib_next_class = 0;
gib_next_handle = 0;
gib_next_handle = 1;
gib_unused_handles = 0;
}

View file

@ -54,6 +54,7 @@ const char rcsid[] =
#include "gib_builtin.h"
#include "gib_thread.h"
#include "gib_handle.h"
#include "gib_object.h"
static void
GIB_Exec_Override_f (void)
@ -109,7 +110,10 @@ GIB_Init (qboolean sandbox)
GIB_Regex_Init ();
// Initialize builtins
GIB_Builtin_Init (sandbox);
// Initialize thread system;
GIB_Thread_Init ();
// Initialize event system
GIB_Event_Init ();
// Initialize object system
GIB_Object_Init ();
}

374
libs/gib/gib_object.c Normal file
View file

@ -0,0 +1,374 @@
/*
gib_object.c
GIB object functions
Copyright (C) 2003 Brian Koropoff
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
static __attribute__ ((unused)) const char rcsid[] =
"$Id$";
#include "QF/hash.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "QF/gib.h"
#include "gib_object.h"
#include "gib_handle.h"
#include "gib_classes.h"
hashtab_t *gib_classes;
/*
Hashtable callbacks
*/
static const char *
GIB_Class_Get_Key (void *ele, void *ptr)
{
return ((gib_class_t *) ele)->name;
}
static void
GIB_Class_Free (void *ele, void *ptr)
{
gib_class_t *b;
b = (gib_class_t *) ele;
free ((void *)b->name);
free (b);
}
static const char *
GIB_Method_Get_Key (void *ele, void *ptr)
{
return ((gib_method_t *) ele)->name;
}
static void
GIB_Method_Free (void *ele, void *ptr)
{
// FIXME: Do something here
}
static const char *
GIB_Signal_Get_Key (void *ele, void *ptr)
{
return ((gib_signal_t *) ele)->name;
}
static void
GIB_Signal_Free (void *ele, void *ptr)
{
gib_signal_t *sig;
gib_slot_t *slot;
sig = (gib_signal_t *) ele;
slot = llist_remove (llist_getnode (sig->receiver->slots,
sig->slot));
free ((void *)sig->name);
free ((void *)slot->mesg);
free (sig);
free (slot);
}
// Linked list callbacks
static void
GIB_Slot_Free (void *ele, void *ptr)
{
gib_signal_t *sig;
gib_slot_t *slot;
slot = (gib_slot_t *) ele;
sig = Hash_DelElement (slot->sender->signals, slot->signal);
free ((void *)sig->name);
free ((void *)slot->mesg);
free (sig);
free (slot);
}
static hashtab_t *
GIB_Method_Build_Hash (gib_class_t *class, hashtab_t *inherited,
gib_methodtab_t *methods)
{
gib_methodtab_t *m;
gib_method_t *method;
hashtab_t *new = Hash_NewTable (1024, GIB_Method_Get_Key, GIB_Method_Free, 0);
for (m = methods; m->name; m++) {
method = malloc (sizeof (gib_method_t));
method->parent = inherited ? Hash_Find (inherited, m->name) : NULL;
method->name = strdup (m->name);
method->func = m->func;
method->data = m->data;
method->class = class;
Hash_Add (new, method);
}
if (inherited) {
void **list;
for (list = Hash_GetList (inherited); *list; list++)
if (!Hash_Find (new, GIB_Method_Get_Key (*list,
NULL)))
Hash_Add (new, *list);
}
return new;
}
void
GIB_Class_Create (gib_classdesc_t *desc)
{
static const char *init = "init";
gib_class_t *parent = NULL, *class = calloc (1, sizeof (gib_class_t));
if (desc->parentname && (parent = Hash_Find (gib_classes, desc->parentname))) {
class->parent = parent;
class->depth = parent->depth + 1;
} else
class->depth = 0;
class->name = strdup (desc->name);
class->construct = desc->construct;
class->class_construct = desc->class_construct;
class->destruct = desc->destruct;
class->methods = GIB_Method_Build_Hash (class, parent ?
parent->methods : NULL, desc->methods);
class->class_methods = GIB_Method_Build_Hash (class, parent ?
parent->class_methods : NULL, desc->class_methods);
Hash_Add (gib_classes, class);
// Create a class object
class->classobj = GIB_Object_Create (desc->name, true);
GIB_Send (class->classobj, 1, &init, NULL, NULL);
}
/*
GIB_Object_Create
Creates a new object from a named class.
Does not send object an initialization
message!
*/
gib_object_t *
GIB_Object_Create (const char *classname, qboolean classobj)
{
gib_class_t *temp, *class = Hash_Find (gib_classes, classname);
gib_object_t *obj;
int i;
if (!class)
return NULL;
obj = calloc (1, sizeof (gib_object_t));
obj->class = class;
obj->data = malloc (sizeof (void *) * (class->depth+1));
obj->methods = classobj ? class->class_methods : class->methods;
obj->handle = classobj ? 0 : GIB_Handle_New (obj);
obj->refs = 1;
obj->signals = Hash_NewTable (128, GIB_Signal_Get_Key,
GIB_Signal_Free, NULL);
obj->slots = llist_new (GIB_Slot_Free, NULL, NULL);
if (classobj) {
for (temp = class, i = class->depth; temp; temp = temp->parent, i--)
if (temp->class_construct)
obj->data[i] = temp->class_construct (obj);
} else {
for (temp = class, i = class->depth; temp; temp = temp->parent, i--)
if (temp->construct)
obj->data[i] = temp->construct (obj);
}
return obj;
}
static void
GIB_Object_Finish_Destroy (int argc, const char **argv, void *data)
{
gib_object_t *obj = (gib_object_t *) data;
int i;
gib_class_t *temp;
for (temp = obj->class, i = obj->class->depth; temp; temp = temp->parent, i--)
if (temp->destruct)
temp->destruct (obj->data[i]);
free (obj->data);
GIB_Handle_Free (obj->handle);
Hash_DelTable (obj->signals);
llist_delete (obj->slots);
free (obj);
}
/*
GIB_Object_Destroy
Sends an object a dispose message and then frees it upon reply.
*/
void
GIB_Object_Destroy (gib_object_t *obj)
{
const static char *dispose = "dispose";
GIB_Send (obj, 1, &dispose, GIB_Object_Finish_Destroy, obj);
}
void
GIB_Object_Incref (gib_object_t *obj)
{
if (obj->refs > 0)
obj->refs++;
}
void
GIB_Object_Decref (gib_object_t *obj)
{
if (obj->refs > 0 && !--obj->refs)
GIB_Object_Destroy (obj);
}
int
GIB_Send (gib_object_t *obj, int argc, const char **argv, gib_reply_handler reply, void *replydata)
{
gib_message_t message;
gib_method_t *method;
if (!(method = Hash_Find (obj->methods, *argv)))
return -1;
message.argc = argc;
message.argv = argv;
message.reply = reply;
message.replydata = replydata;
return method->func (obj, method, obj->data[method->class->depth], message);
}
int
GIB_SendToMethod (gib_object_t *obj, gib_method_t *method, int argc, const char **argv, gib_reply_handler reply, void *replydata)
{
gib_message_t message;
message.argc = argc;
message.argv = argv;
message.reply = reply;
message.replydata = replydata;
return method->func (obj, method, obj->data[method->class->depth], message);
}
gib_object_t *
GIB_Object_Get (const char *id)
{
gib_class_t *class;
if (isdigit ((byte) *id))
return GIB_Handle_Get (atoi (id));
else if ((class = Hash_Find (gib_classes, id)))
return class->classobj;
else
return NULL;
}
void
GIB_Object_Signal_Slot_Pair (gib_object_t *sender, const char *signal,
gib_object_t *receiver, const char *slot)
{
gib_signal_t *si = malloc (sizeof (gib_signal_t));
gib_slot_t *sl = malloc (sizeof (gib_slot_t));
si->slot = sl;
sl->signal = si;
si->receiver = receiver;
sl->sender = sender;
si->name = strdup (signal);
sl->mesg = strdup (slot);
Hash_Add (sender->signals, si);
llist_append (receiver->slots, sl);
}
void
GIB_Object_Signal_Slot_Destroy (gib_object_t *sender, const char *signal,
gib_object_t *receiver, const char *slot)
{
gib_signal_t **list, **cur;
if ((list = (gib_signal_t **) Hash_FindList (sender->signals, signal))) {
for (cur = list; *cur; cur++) {
if ((*cur)->receiver == receiver && !strcmp
((*cur)->slot->mesg, slot)) {
Hash_Free (sender->signals, Hash_DelElement
(sender->signals, *cur));
break;
}
}
free (list);
}
}
void
GIB_Object_Signal_Emit (gib_object_t *sender, int argc, const char **argv)
{
gib_signal_t **list, **cur;
const char *old = *argv;
if ((list = (gib_signal_t **) Hash_FindList (sender->signals, *argv))) {
for (cur = list; *cur; cur++) {
*argv = (*cur)->slot->mesg;
GIB_Send ((*cur)->receiver, argc, argv, NULL, NULL);
}
free (list);
}
*argv = old;
}
void
GIB_Object_Init (void)
{
gib_classes = Hash_NewTable (1024, GIB_Class_Get_Key, GIB_Class_Free, 0);
GIB_Classes_Init ();
}

View file

@ -35,6 +35,80 @@
#include "gib_parse.h"
#include "gib_semantics.h"
static int
GIB_Semantic_Validate_Class (gib_tree_t * tokens)
{
gib_tree_t *a_class, *line, *cmd;
if (!tokens->next || !tokens->next->next) {
GIB_Parse_Error ("Malformed class definition; expected class "
"name, optional colon and parent class, and "
"program block.", tokens->start);
return -1;
}
if (tokens->next->next->delim == ' ' && !strcmp
(tokens->next->next->str, ":")) {
if (!tokens->next->next->next) {
GIB_Parse_Error ("Malformed class definition; "
"expected parent class after "
"\":\".", tokens->next->next->start);
return -1;
}
a_class = tokens->next->next->next->next;
} else
a_class = tokens->next->next;
if (!a_class || !a_class->children || a_class->delim != '{') {
GIB_Parse_Error ("Malformed class definition; expected "
"program block.", tokens->next->next->start);
return -1;
}
for (line = a_class->children; line; line = line->next) {
switch (line->type) {
case TREE_T_LABEL:
if (strcmp (line->str, "class") && strcmp
(line->str, "instance")) {
GIB_Parse_Error ("Malformed class "
"definition; allowed "
"labels are instance "
"and class.",
line->start);
return -1;
}
break;
case TREE_T_CMD:
cmd = line->children;
if (strcmp (cmd->str, "function")) {
GIB_Parse_Error ("Malformed class "
"definition; only "
"allowed command is "
"function.",
cmd->start);
return -1;
} else if (!cmd->next || !cmd->next->next ||
cmd->next->next->delim != '{'
|| !cmd->next->next->children) {
GIB_Parse_Error ("Malformed function "
"definition; name, "
"program block "
"expected.",
cmd->start);
return -1;
}
break;
default:
GIB_Parse_Error ("Malformed class "
"definition; only commands "
"and labels allowed.",
line->start);
return -1;
break;
}
}
return 0;
}
static gib_tree_t *
GIB_Semantic_Normal_To_Lines (gib_tree_t * tokens, const char *program, gib_tree_flags_t flags, unsigned int start, unsigned int end)
{
@ -72,11 +146,25 @@ GIB_Semantic_Normal_To_Lines (gib_tree_t * tokens, const char *program, gib_tree
mainline->flags = flags;
mainline->start = start;
mainline->end = end;
mainline->children = tokens;
// Check for assignment
if (tokens->next && tokens->next->delim == ' ' && !strcmp (tokens->next->str, "="))
mainline->type = TREE_T_ASSIGN;
// Check for message sending
else if (tokens->next && tokens->next->delim == ' ' && !strcmp (tokens->next->str, "->")) {
if (!tokens->next->next) {
GIB_Tree_Unref (&mainline);
GIB_Parse_Error ("Cannot send empty message.", token->next->start);
return NULL;
} else
mainline->type = TREE_T_SEND;
// Check for class definition, validate
} else if (!strcmp (tokens->str, "class") &&
GIB_Semantic_Validate_Class (tokens)) {
GIB_Tree_Unref (&mainline);
return NULL;
}
mainline->children = tokens;
return lines;
}
@ -252,7 +340,7 @@ GIB_Semantic_While_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_f
next = &conditional->next;
// Create end point (for 'break' commands)
endp = GIB_Tree_New (TREE_T_NOP);
endp = GIB_Tree_New (TREE_T_LABEL);
// Move program block inline
*next = a_block->children;
@ -358,7 +446,7 @@ GIB_Semantic_For_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_fla
*next = a_block->children;
a_block->children = 0;
endp = GIB_Tree_New (TREE_T_NOP);
endp = GIB_Tree_New (TREE_T_LABEL);
// Find end of program block
for (temp = *next; temp->next; temp = temp->next) {
@ -384,6 +472,24 @@ GIB_Semantic_For_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_fla
return lines;
}
static gib_tree_t *
GIB_Semantic_Label_To_Lines (gib_tree_t *tokens, const char *program,
gib_tree_flags_t flags)
{
gib_tree_t *line;
char *name;
line = GIB_Tree_New (TREE_T_LABEL);
name = strdup (tokens->str);
name[strlen(name)-1] = '\0';
line->str = name;
line->flags = flags;
GIB_Tree_Unref (&tokens);
return line;
}
gib_tree_t *
GIB_Semantic_Tokens_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_flags_t flags, unsigned int start, unsigned int end)
@ -399,7 +505,7 @@ GIB_Semantic_Tokens_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_
// Create landing pad where all program blocks in
// a chained if/else structure will jump to after
// completing so that only one block is executed.
endp = GIB_Tree_New (TREE_T_NOP);
endp = GIB_Tree_New (TREE_T_LABEL);
do {
// Link in output from if statement handler
*next = GIB_Semantic_If_To_Lines (&tokens, program, flags, endp);
@ -419,6 +525,8 @@ GIB_Semantic_Tokens_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_
*next = GIB_Semantic_While_To_Lines (tokens, program, flags);
else if (!strcmp (tokens->str, "for"))
*next = GIB_Semantic_For_To_Lines (tokens, program, flags);
else if (tokens->str[strlen(tokens->str)-1] == ':' && !tokens->next)
*next = GIB_Semantic_Label_To_Lines (tokens, program, flags);
else
*next = GIB_Semantic_Normal_To_Lines (tokens, program, flags, start, end);
next = &(*next)->next;

View file

@ -44,88 +44,67 @@ const char rcsid[] = "$Id$";
#include "QF/gib.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/llist.h"
#include "gib_handle.h"
#include "gib_tree.h"
#include "gib_function.h"
#include "gib_thread.h"
gib_thread_t *gib_thread_first = 0;
gib_thread_t *gib_thread_last = 0;
unsigned short int gib_thread_class;
llist_t *gib_threads;
hashtab_t *gib_events;
void
GIB_Thread_Add (gib_thread_t * thread)
static void
GIB_Thread_Free (void *ele, void *data)
{
if (!gib_thread_first)
gib_thread_first = thread;
thread->prev = gib_thread_last;
if (!gib_thread_last)
gib_thread_last = thread;
else {
gib_thread_last->next = thread;
gib_thread_last = thread;
}
Cbuf_DeleteStack ((cbuf_t *) ele);
}
void
GIB_Thread_Remove (gib_thread_t * thread)
{
if (thread->prev)
thread->prev->next = thread->next;
else
gib_thread_first = thread->next;
if (thread->next)
thread->next->prev = thread->prev;
else
gib_thread_last = thread->prev;
}
gib_thread_t *
cbuf_t *
GIB_Thread_New (void)
{
gib_thread_t *new = calloc (1, sizeof (gib_thread_t));
new->cbuf = Cbuf_New (GIB_Interpreter ());
new->id = GIB_Handle_New (new, gib_thread_class);
cbuf_t *new = Cbuf_New (GIB_Interpreter ());
llist_append (gib_threads, new);
return new;
}
void
GIB_Thread_Delete (gib_thread_t * thread)
GIB_Thread_Delete (cbuf_t *thread)
{
Cbuf_DeleteStack (thread->cbuf);
GIB_Handle_Free (thread->id, gib_thread_class);
free (thread);
cbuf_t *temp;
for (temp = thread; temp->down && temp->down->state != CBUF_STATE_JUNK; temp = temp->down);
if (temp == cbuf_active)
temp->state = CBUF_STATE_ERROR;
else
llist_remove (llist_getnode (gib_threads, thread));
}
unsigned int
GIB_Thread_Count (void)
{
return llist_size (gib_threads);
}
void
GIB_Thread_Execute (void)
{
gib_thread_t *cur, *tmp;
if (!gib_thread_first)
return;
for (cur = gib_thread_first; cur; cur = tmp) {
tmp = cur->next;
if (GIB_DATA(cur->cbuf)->program)
Cbuf_Execute_Stack (cur->cbuf);
else {
GIB_Thread_Remove (cur);
GIB_Thread_Delete (cur);
}
static qboolean iterator (cbuf_t *cbuf, llist_node_t *node)
{
if (GIB_DATA(cbuf)->program)
Cbuf_Execute_Stack (cbuf);
else
Cbuf_DeleteStack ((cbuf_t *) llist_remove (node));
return true;
}
llist_iterate (gib_threads, LLIST_ICAST (iterator));
}
void
GIB_Thread_Init (void)
{
gib_thread_class = GIB_Handle_Class_New ();
gib_threads = llist_new (GIB_Thread_Free, NULL, NULL);
}
static const char *
@ -169,7 +148,7 @@ void
GIB_Event_Callback (gib_event_t * event, unsigned int argc, ...)
{
gib_function_t *f = event->func;
gib_thread_t *thread;
cbuf_t *thread;
cbuf_args_t *args;
va_list ap;
unsigned int i;
@ -188,8 +167,7 @@ GIB_Event_Callback (gib_event_t * event, unsigned int argc, ...)
va_end (ap);
GIB_Function_Execute_D (thread->cbuf, f, args->argv, args->argc);
GIB_Thread_Add (thread);
GIB_Function_Execute_D (thread, f, args->argv, args->argc);
Cbuf_ArgsDelete (args);
}

View file

@ -302,6 +302,12 @@ GIB_Domain_Get (const char *name)
return d->vars;
}
hashtab_t *
GIB_Var_Hash_New (void)
{
return Hash_NewTable (1024, GIB_Var_Get_Key, GIB_Var_Free, 0);
}
void
GIB_Var_Init (void)
{

View file

@ -51,7 +51,7 @@ static __attribute__ ((unused)) const char rcsid[] =
#include "compat.h"
cbuf_t *cbuf_active;
cbuf_t *cbuf_active = NULL;
cbuf_args_t *
Cbuf_ArgsNew (void)
@ -175,8 +175,10 @@ Cbuf_InsertText (cbuf_t *cbuf, const char *text)
void
Cbuf_Execute (cbuf_t *cbuf)
{
cbuf_t *old = cbuf_active;
cbuf_active = cbuf;
cbuf->interpreter->execute (cbuf);
cbuf_active = old;
}
void
@ -191,6 +193,8 @@ Cbuf_Execute_Stack (cbuf_t *cbuf)
return;
}
for (sp = cbuf; sp->down && sp->down->state != CBUF_STATE_JUNK; sp = sp->down);
if (sp->state == CBUF_STATE_BLOCKED)
return;
while (sp) {
Cbuf_Execute (sp);
if (sp->state) {
@ -200,6 +204,8 @@ Cbuf_Execute_Stack (cbuf_t *cbuf)
continue;
} else if (sp->state == CBUF_STATE_ERROR)
break;
else if (sp->state == CBUF_STATE_BLOCKED)
return;
else {
sp->state = CBUF_STATE_NORMAL;
return;

View file

@ -28,6 +28,12 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
@ -195,6 +201,19 @@ llist_remove (llist_node_t *ref)
return element;
}
unsigned int
llist_size (llist_t *llist)
{
unsigned int i;
llist_node_t *n;
if (!llist->start)
return 0;
else for (i = 0, n = llist->start; n; n = n->next, i++);
return i;
}
void
llist_iterate (llist_t *list, llist_iterator_t iterate)
{
@ -228,7 +247,7 @@ llist_findnode (llist_t *list, void *comparison)
{
llist_node_t *node;
if (!list)
if (!list || !list->cmpdata)
return 0;
for (node = list->start; node; node = node->next)
if (list->cmpdata (node->data, comparison, list->userdata))
@ -236,3 +255,15 @@ llist_findnode (llist_t *list, void *comparison)
return 0;
}
void *
llist_createarray (llist_t *list, size_t esize)
{
void *ptr, *array = malloc (llist_size (list) * esize);
llist_node_t *node;
for (ptr = array, node = list->start; node; node = node->next, ptr +=
esize)
memcpy (ptr, node->data, esize);
return array;
}

View file

@ -74,8 +74,8 @@ common_LIBFILES= \
$(top_builddir)/libs/gamecode/builtins/libQFgamecode_builtins.la \
$(top_builddir)/libs/gamecode/engine/libQFgamecode.la \
$(top_builddir)/libs/image/libQFimage.la \
$(top_builddir)/libs/util/libQFutil.la \
$(top_builddir)/libs/gib/libQFgib.la
$(top_builddir)/libs/gib/libQFgib.la \
$(top_builddir)/libs/util/libQFutil.la
client_LIBS= $(client_LIBFILES) $(common_LIBFILES)

View file

@ -80,8 +80,8 @@ qf_server_LIBS= \
$(top_builddir)/libs/gamecode/builtins/libQFgamecode_builtins.la \
$(top_builddir)/libs/gamecode/engine/libQFgamecode.la \
$(top_builddir)/libs/console/libQFconsole.la \
$(top_builddir)/libs/util/libQFutil.la \
$(top_builddir)/libs/gib/libQFgib.la
$(top_builddir)/libs/gib/libQFgib.la \
$(top_builddir)/libs/util/libQFutil.la
qw_server_SOURCES= sv_model.c
qw_server_LDADD= libqw_server.a libqw_common.a libasm.a $(qf_server_LIBS) $(NET_LIBS) $(DL_LIBS) $(CURSES_LIBS)

View file

@ -9,7 +9,6 @@
integer handle;
}
+ (void) initClass;
- (id) initWithComponents: (integer) x : (integer) y;
- (void) dealloc;
- (integer) handle;

View file

@ -8,17 +8,11 @@
integer HUDHandleClass;
@implementation HUDObject
+ (void) initClass
{
HUDHandleClass = GIB_Handle_Class_New ();
}
- (id) initWithComponents: (integer) x : (integer) y
{
self = [super init];
origin = [[Point alloc] initWithComponents: x :y];
visible = YES;
handle = GIB_Handle_New (self, HUDHandleClass);
return self;
}
@ -26,7 +20,6 @@ integer HUDHandleClass;
- (void) dealloc
{
[origin dealloc];
GIB_Handle_Free (handle, HUDHandleClass);
[super dealloc];
}

View file

@ -508,5 +508,4 @@ void () menu_init =
Menu_TopMenu ("main");
Menu_SetQuit (quit);
HUD_Init ();
};

View file

@ -5,271 +5,7 @@
#include "string.h"
#include "debug.h"
@static Array HUDObjects;
void (integer argc, string [] argv) gib_hud_new_text_f =
{
local HUDObject newText;
if (argc != 4)
return;
newText = [[HUDText alloc] initWithComponents
:stoi (argv[1])
:stoi (argv[2])
:argv[3]
];
[HUDObjects addItem :newText];
GIB_Return (itos ([newText handle]));
};
void (integer argc, string [] argv) gib_hud_new_graphic_f =
{
local HUDGraphic newGraphic;
if (argc != 4)
return;
newGraphic = [[HUDGraphic alloc] initWithComponents
:stoi (argv[1])
:stoi (argv[2])
:argv[3]
];
[HUDObjects addItem :newGraphic];
GIB_Return (itos ([newGraphic handle]));
};
void (integer argc, string [] argv) gib_hud_new_anim_f =
{
local HUDAnimation newAnim;
local Frame frame;
local integer i;
if (argc < 5)
return;
newAnim = [[HUDAnimation alloc] initWithComponents
:stoi (argv[1])
:stoi (argv[2])
];
for (i = 3; i < argc; i += 2) {
frame = [[Frame alloc] initWithFile: argv[i] duration: stof (argv[i+1])];
[newAnim addFrame :frame];
}
[HUDObjects addItem :newAnim];
GIB_Return (itos ([newAnim handle]));
};
void (integer argc, string [] argv) gib_hud_start_anim_f =
{
local integer i;
local HUDAnimation anim;
if (argc < 2)
return;
for (i = 1; i < argc; i++) {
anim = GIB_Handle_Get (stoi (argv[i]), HUDHandleClass);
if (anim && [anim isKindOfClass :[HUDAnimation class]])
[anim start];
}
};
void (integer argc, string [] argv) gib_hud_stop_anim_f =
{
local integer i;
local HUDAnimation anim;
if (argc < 2)
return;
for (i = 1; i < argc; i++) {
anim = GIB_Handle_Get (stoi (argv[i]), HUDHandleClass);
if (anim && [anim isKindOfClass :[HUDAnimation class]])
[anim stop];
}
};
void (integer argc, string [] argv) gib_hud_set_looping_f =
{
local HUDAnimation anim;
if (argc < 3)
return;
anim = GIB_Handle_Get (stoi (argv[1]), HUDHandleClass);
if (anim && [anim isKindOfClass :[HUDAnimation class]]) {
switch (argv[2]) {
case "yes":
case "true":
[anim setLooping :YES];
break;
case "no":
case "false":
[anim setLooping :NO];
break;
default:
break;
}
}
};
void (integer argc, string [] argv) gib_hud_delete_f =
{
local integer i;
local HUDObject trashObject;
if (argc < 2)
return;
for (i = 1; i < argc; i++) {
trashObject = GIB_Handle_Get (stoi (argv[i]), HUDHandleClass);
if (trashObject) {
[HUDObjects removeItem :trashObject];
[trashObject dealloc];
} else
dprint (sprintf ("Warning: no HUD object associated with handle %i\n", stoi (argv[i])));
}
};
void (integer argc, string [] argv) gib_hud_get_rect_f =
{
local HUDObject myObject;
local Point myPoint;
if (argc != 2)
return;
myObject = GIB_Handle_Get (stoi (argv[1]), HUDHandleClass);
if (!myObject)
return;
myPoint = [myObject origin];
GIB_Return (itos ([myPoint x]));
GIB_Return (itos ([myPoint y]));
myPoint = [myObject size];
GIB_Return (itos ([myPoint x]));
GIB_Return (itos ([myPoint y]));
[myPoint dealloc];
};
void (integer argc, string [] argv) gib_hud_set_translate_f =
{
local integer i;
local Point p;
if (argc < 4)
return;
p = [[Point alloc] initWithComponents :stoi (argv[argc-2]) :stoi (argv[argc-1])];
if (argv[0] == "HUD::translate")
for (i = 1; i < argc - 2; i++)
[(HUDObject) GIB_Handle_Get (stoi (argv[i]), HUDHandleClass) translate :p];
else
for (i = 1; i < argc - 2; i++)
[(HUDObject) GIB_Handle_Get (stoi (argv[i]), HUDHandleClass) setOrigin :p];
[p dealloc];
};
void (integer argc, string [] argv) gib_hud_set_text_f =
{
local HUDText myObject;
if (argc != 3)
return;
myObject = GIB_Handle_Get (stoi (argv[1]), HUDHandleClass);
if (!myObject || ![myObject isKindOfClass :[HUDText class]])
return;
[myObject setText :argv[2]];
};
void (integer argc, string [] argv) gib_hud_set_file_f =
{
local HUDGraphic myObject;
if (argc != 3)
return;
myObject = GIB_Handle_Get (stoi (argv[1]), HUDHandleClass);
if (!myObject || ![myObject isKindOfClass :[HUDGraphic class]])
return;
[myObject setFile :argv[2]];
};
void (integer argc, string [] argv) gib_hud_show_f =
{
local integer i;
local HUDObject object;
if (argc < 2)
return;
for (i = 1; i < argc; i++) {
object = GIB_Handle_Get (stoi (argv[i]), HUDHandleClass);
if (object)
[object setVisible :YES];
else
dprint (sprintf ("Warning: no HUD object associated with handle %i\n", stoi (argv[i])));
}
};
void (integer argc, string [] argv) gib_hud_hide_f =
{
local integer i;
local HUDObject object;
if (argc < 2)
return;
for (i = 1; i < argc; i++) {
object = GIB_Handle_Get (stoi (argv[i]), HUDHandleClass);
if (object)
[object setVisible :NO];
else
dprint (sprintf ("Warning: no HUD object associated with handle %i\n", stoi (argv[i])));
}
};
void () HUD_Init =
{
GIB_Builtin_Add ("HUD::newText", gib_hud_new_text_f);
GIB_Builtin_Add ("HUD::newGraphic", gib_hud_new_graphic_f);
GIB_Builtin_Add ("HUD::newAnim", gib_hud_new_anim_f);
GIB_Builtin_Add ("HUD::delete", gib_hud_delete_f);
GIB_Builtin_Add ("HUD::show", gib_hud_show_f);
GIB_Builtin_Add ("HUD::hide", gib_hud_hide_f);
GIB_Builtin_Add ("HUD::getRect", gib_hud_get_rect_f);
GIB_Builtin_Add ("HUD::setOrigin", gib_hud_set_translate_f);
GIB_Builtin_Add ("HUD::translate", gib_hud_set_translate_f);
GIB_Builtin_Add ("HUD::setText", gib_hud_set_text_f);
GIB_Builtin_Add ("HUD::setFile", gib_hud_set_file_f);
GIB_Builtin_Add ("HUD::startAnim", gib_hud_start_anim_f);
GIB_Builtin_Add ("HUD::stopAnim", gib_hud_stop_anim_f);
GIB_Builtin_Add ("HUD::setLooping", gib_hud_set_looping_f);
// Initialize HUDObject class
[HUDObject initClass];
// Create array of HUD objects
HUDObjects = [[Array alloc] init];
};
void () menu_draw_hud =
{
local integer i;
local HUDObject myHUDObject;
for (i = 0; i < [HUDObjects count]; i++) {
myHUDObject = [HUDObjects getItemAt :i];
[myHUDObject display];
}
};

View file

@ -3,9 +3,5 @@
@extern void (string name, void (integer argc, string [] argv) func) GIB_Builtin_Add;
@extern integer (string value) GIB_Return;
@extern integer (void) GIB_Handle_Class_New = #0;
@extern integer (void [] data, integer class) GIB_Handle_New = #0;
@extern void [] (integer handle, integer class) GIB_Handle_Get = #0;
@extern void (integer handle, integer class) GIB_Handle_Free = #0;
#endif//__ruamoko_gib_h

View file

@ -2,7 +2,3 @@
void (string name, void (integer argc, string [] argv) func) GIB_Builtin_Add = #0;
integer (string value) GIB_Return = #0;
integer (void) GIB_Handle_Class_New = #0;
integer (void [] data, integer class) GIB_Handle_New = #0;
void [] (integer handle, integer class) GIB_Handle_Get = #0;
void (integer handle, integer class) GIB_Handle_Free = #0;

View file

@ -17,8 +17,6 @@
#include "gib_thread.h"
#include "gib_parse.h"
extern gib_thread_t *gib_threads;
static qboolean carne_done = false;
static int carne_exitcode = 0;
@ -116,6 +114,7 @@ main (int argc, char **argv)
Cvar_Init_Hash ();
Cmd_Init_Hash ();
Cmd_Init ();
Cvar_Init ();
GIB_Init (false); // No sandbox
GIB_Builtin_Add ("exit", Carne_GIB_Exit_f);