Embedding/GC/heap corruption problem

Hi,

I’m working on adding Ruby scripting to the Half-Life game engine, similar
to several other projects, like AMXmod (http://amxmod.net). Unfortunately
I’m running into problems which seem to be related to garbage collection.
I think I have to give a short overview on how the interaction between the
game engine and the scripting engine is structured.

The Half-Life engine communicates with clients by sending messages. One
purpose of the scripting framework is to intercept those messages and run Ruby
code in reaction to these events.

The game engine calls the C functions MessageBegin, MessageEnd and several
other functions to store data inside a message like WriteShort and WriteString.
MessageBegin initiates a new messages, afterwards WriteXXX function calls are
made to attach data. MessageEnd terminates the message. The C part of the
scripting engine catches these function calls and utilizes a global (in C) Ruby
variable to store the message’s data.

Errors (segfaults) occur on execution of the message dispatching code of the
Ruby part (MessageRegistry.dispatch_message). It looks like the GC
reaps the message’s ‘args’ instance variable - an array containing the message
data - even though its referenced by the global message variable. Surprisingly
it happens rather seldom, say between every 1000’s and 10000’s message
dispatch. The problem does not occur when I manually do garbage collections
on a very regular basis.

An error case looks like this:

L 05/14/2003 - 22:41:15: [RUBY] Message count: 1039
L 05/14/2003 - 22:41:15: [RUBY] Message type: StatusValue (107)
L 05/14/2003 - 22:41:15: [RUBY] Message args: [1,1]
L 05/14/2003 - 22:41:15: [RUBY] Message receiver: 3, destination: 1
L 05/14/2003 - 22:41:15: [RUBY] Message count: 1040
L 05/14/2003 - 22:41:15: [RUBY] Message type: StatusValue (107)
L 05/14/2003 - 22:41:15: [RUBY] Message args: [2,1]
L 05/14/2003 - 22:41:15: [RUBY] Message receiver: 3, destination: 1
cstrike/addons/halfruby/kernel/message_dispatch.rb:26: [BUG] Segmentation fault
ruby 1.6.7 (2002-03-19) [i386-linux]

Stacktrace case 1:

(gdb) bt
#0 0x400a4fd1 in kill () from /lib/libc.so.6
#1 0x40036762 in raise () from /lib/libpthread.so.0
#2 0x400a4ca2 in raise () from /lib/libc.so.6
#3 0x400a604d in abort () from /lib/libc.so.6
#4 0x43a67cd9 in rb_bug () at error.c:178
#5 0x43ab4acf in sigsegv () at signal.c:393
#6 0x40039929 in __pthread_sighandler () from /lib/libpthread.so.0
#7
#8 0x43a6b16d in jump_tag_but_local_jump (state=6) at eval.c:1314
#9 0x43a731ff in rb_call0 (klass=1138029288, recv=1138014008, id=9313, argc=0, argv=0xbfffec6c, body=0x43d4bbd4, nosuper=0) at eval.c:4542
#10 0x43a73584 in rb_call (klass=1138029288, recv=1138014008, mid=9313, argc=1, argv=0xbfffec68, scope=1) at eval.c:4606
#11 0x43a73857 in rb_funcall (recv=1138014008, mid=9313, n=1) at ruby.h:571
#12 0x43a5dee5 in MessageEnd() () at messages.cpp:31
#13 0x43772cfe in mm_MessageEnd () from ./cstrike/addons/metamod/dlls/metamod_i386.so

Stacktrace case 2:

(gdb) bt
#0 0x400a4fd1 in kill () from /lib/libc.so.6
#1 0x40036762 in raise () from /lib/libpthread.so.0
#2 0x400a4ca2 in raise () from /lib/libc.so.6
#3 0x400a604d in abort () from /lib/libc.so.6
#4 0x43a67cd9 in rb_bug () at error.c:178
#5 0x43ab4acf in sigsegv () at signal.c:393
#6 0x40039929 in __pthread_sighandler () from /lib/libpthread.so.0
#7
#8 0x00000011 in ?? ()
#9 0x43a6961d in search_method (klass=1137889328, id=3001, origin=0xbfffd978) at eval.c:247
#10 0x43a69672 in rb_get_method_body (klassp=0xbfffd9c4, idp=0xbfffd9a8, noexp=0xbfffd9ac) at eval.c:268
#11 0x43a734cc in rb_call (klass=1137889328, recv=1137889348, mid=3001, argc=0, argv=0x0, scope=1) at eval.c:4583
#12 0x43a73857 in rb_funcall (recv=1137889348, mid=3001, n=0) at ruby.h:571
#13 0x43ab7cee in rb_obj_as_string (obj=1137889348) at string.c:191
#14 0x43a6efb8 in rb_eval (self=1138014008, n=0x0) at eval.c:2895
#15 0x43a6e587 in rb_eval (self=1138014008, n=0x0) at ruby.h:576
#16 0x43a6cafc in rb_eval (self=1138014008, n=0x0) at eval.c:2028
#17 0x43a6cafc in rb_eval (self=1138014008, n=0x0) at eval.c:2028
#18 0x43a730a9 in rb_call0 (klass=1138029288, recv=1138014008, id=9313, argc=0, argv=0xbffff1d8, body=0x43d4bbd4, nosuper=0) at eval.c:4513
#19 0x43a73584 in rb_call (klass=1138029288, recv=1138014008, mid=9313, argc=1, argv=0xbffff1d4, scope=1) at eval.c:4606
#20 0x43a73857 in rb_funcall (recv=1138014008, mid=9313, n=1) at ruby.h:571
#21 0x43a5dee5 in MessageEnd() () at messages.cpp:31
#22 0x43772cfe in mm_MessageEnd () from ./cstrike/addons/metamod/dlls/metamod_i386.so

(Case 1 and 2 differ in changed statement order in dispatch_message in the
‘if (@verbose)’ block, case 1 crashes on logging the @message_count first,
case 2 on logging the @args array first.)

I hope you’re able to follow my descriptions and understand what’s basically
going on when reading the attached sources. I wonder if there is some more
or less obvious error I’m doing here, since I’m pretty new to Ruby extensions
and embedding.

I know the description is far from good and feel free to ask any question you
have with understanding the problem. If anybody is interested in full sources
feel free to ask also. Wow, what a confused post that is.

Any hints very appreciated since I’m pretty stuck.

  • Jan.

The relevant code parts follow (I left out some #include’s and maybe some
unimportant code).

messages.cpp

···

static VALUE message = Qnil;
static VALUE args = Qnil;
static VALUE message_registry = Qnil;

void messages_init() {
rb_global_variable(&message);
message = rb_eval_string(“Message.new”);
args = rb_iv_get(message, “@args”);
message_registry = rb_eval_string(“MessageRegistry.instance”);
}

void MessageBegin(int dest, int _msg_type, const float *origin, edict_t *edict) {
rb_funcall(args, rb_intern(“clear”), 0);
rb_iv_set(message, “@message_type”, INT2FIX(_msg_type));
rb_iv_set(message, “@destination”, INT2FIX(dest));
rb_iv_set(message, “@receiver”, INT2FIX(ENTINDEX(edict)));
RETURN_META(MRES_IGNORED);
}

void MessageEnd(void) {
rb_funcall(message_registry, rb_intern(“dispatch_message”), 1, message);
RETURN_META(MRES_IGNORED);
}

void WriteByte(int value) {
rb_ary_push(args, INT2FIX(value));
RETURN_META(MRES_IGNORED);
}

void WriteChar(int value) {
rb_ary_push(args, INT2FIX(value));
RETURN_META(MRES_IGNORED);
}

void WriteShort(int value) {
rb_ary_push(args, INT2FIX(value));
RETURN_META(MRES_IGNORED);
}

void WriteLong(int value) {
rb_ary_push(args, INT2NUM(value));
RETURN_META(MRES_IGNORED);
}

void WriteAngle(float value) {
rb_ary_push(args, rb_float_new(value));
RETURN_META(MRES_IGNORED);
}

void WriteCoord(float value) {
rb_ary_push(args, rb_float_new(value));
RETURN_META(MRES_IGNORED);
}

void WriteString(const char *s) {
rb_ary_push(args, rb_str_new2(s));
RETURN_META(MRES_IGNORED);
}

void WriteEntity(int value) {
rb_ary_push(args, INT2NUM(value));
RETURN_META(MRES_IGNORED);
}

message_dispatch.rb

class MessageRegistry
include Singleton

attr_accessor :verbose, :message_count 

def initialize
@registry = {}
@message_count = 0
@verbose = true
end

def register_for(message_name, &block)
id = Game.get_message_id(message_name)
Game.log "Registering for message id #{id}"
if (! @registry.has_key? id)
    @registry[id] = []
end
@registry[id] << block 
end

def dispatch_message(message)
@message_count += 1  
if (@verbose) 
    Game.log "Message count: #{@message_count}"
    Game.log "Message type: #{message.message_name} (#{message.message_type})"
    Game.log "Message args: [#{message.args.join ','}]"
    Game.log "Message receiver: #{message.receiver}, destination: #{message.destination}"
end

if (@registry.has_key? message.message_type)
    @registry[message.message_type].each { |block| block.call(message) }
end
end

end

class Message
attr_accessor :message_type, :destination, :receiver, :args

def initialize
@args = []
@message_type = nil
@destination = nil
@receiver = nil
end

def message_name
Game.get_message_name(@message_type)
end

end

rb_game.cpp

[…]

VALUE rb_game_log(VALUE self, VALUE message) {
UTIL_LogPrintf(“[%s] %s\n”, MODULE_TAG, STR2CSTR(rb_obj_as_string(message)));
return Qnil;
}

void rb_game_init() {
rb_game_module = rb_define_module(“Game”);

[...]

rb_define_module_function(rb_game_module, "log", RUBY_METHOD_FUNC(rb_game_log), 1);

[...]

}

[…]

void MessageEnd(void) { rb_funcall(message_registry, rb_intern("dispatch_message"), 1, message); RETURN_META(MRES_IGNORED); }

you don’t wrap rb_funcall() within a rb_protect()…
Im guessing this is part of the reason.

see - how to use rb_protect() warning: c++
http://ruby-talk.org/71421

···

On Wed, 14 May 2003 23:55:22 +0200, Jan Bernhardt wrote:

Stacktrace case 1:

#8 0x43a6b16d in jump_tag_but_local_jump (state=6) at eval.c:1314
#9 0x43a731ff in rb_call0 (klass=1138029288, recv=1138014008, id=9313, argc=0, argv=0xbfffec6c, body=0x43d4bbd4, nosuper=0) at eval.c:4542
#10 0x43a73584 in rb_call (klass=1138029288, recv=1138014008, mid=9313, argc=1, argv=0xbfffec68, scope=1) at eval.c:4606
#11 0x43a73857 in rb_funcall (recv=1138014008, mid=9313, n=1) at ruby.h:571
#12 0x43a5dee5 in MessageEnd() () at messages.cpp:31
#13 0x43772cfe in mm_MessageEnd () from ./cstrike/addons/metamod/dlls/metamod_i386.so

Stacktrace case 2:
#18 0x43a730a9 in rb_call0 (klass=1138029288, recv=1138014008, id=9313, argc=0, argv=0xbffff1d8, body=0x43d4bbd4, nosuper=0) at eval.c:4513
#19 0x43a73584 in rb_call (klass=1138029288, recv=1138014008, mid=9313, argc=1, argv=0xbffff1d4, scope=1) at eval.c:4606
#20 0x43a73857 in rb_funcall (recv=1138014008, mid=9313, n=1) at ruby.h:571
#21 0x43a5dee5 in MessageEnd() () at messages.cpp:31
#22 0x43772cfe in mm_MessageEnd () from ./cstrike/addons/metamod/dlls/metamod_i386.so


Simon Strandgaard

I used to do that, but during experimentation I removed the protection
wrapper. But of course you’re right - I should protect the function call.

  • Jan.
···

On Wed, 14 May 2003 23:27:39 +0200, Simon Strandgaard wrote:

void MessageEnd(void) { rb_funcall(message_registry, rb_intern("dispatch_message"), 1, message); RETURN_META(MRES_IGNORED); }

you don’t wrap rb_funcall() within a rb_protect()…
Im guessing this is part of the reason.

see - how to use rb_protect() warning: c++
http://ruby-talk.org/71421

After looking promising it bombed again:

L 05/14/2003 - 23:23:38: [RUBY] Message count: 2692
L 05/14/2003 - 23:23:38: [RUBY] Message type: tempentity? (23)
cstrike/addons/halfruby/kernel/message_dispatch.rb:28: [BUG] Segmentation fault
ruby 1.6.7 (2002-03-19) [i386-linux]

(which is during executing

Game.log "Message args: [#{message.args.join ','}]"

)

(gdb) bt
#0 0x400c5fd1 in kill () from /lib/libc.so.6
#1 0x40036762 in raise () from /lib/libpthread.so.0
#2 0x400c5ca2 in raise () from /lib/libc.so.6
#3 0x400c704d in abort () from /lib/libc.so.6
#4 0x43a67d71 in rb_bug () at error.c:178
#5 0x43ab4b67 in sigsegv () at signal.c:393
#6 0x40039929 in __pthread_sighandler () from /lib/libpthread.so.0
#7
#8 0x00000011 in ?? ()
#9 0x43a696b5 in search_method (klass=1137889328, id=3001, origin=0xbfffd884) at eval.c:247
#10 0x43a6970a in rb_get_method_body (klassp=0xbfffd8d0, idp=0xbfffd8b4, noexp=0xbfffd8b8) at eval.c:268
#11 0x43a73564 in rb_call (klass=1137889328, recv=1137889348, mid=3001, argc=0, argv=0x0, scope=1) at eval.c:4583
#12 0x43a738ef in rb_funcall (recv=1137889348, mid=3001, n=0) at ruby.h:571
#13 0x43ab7d86 in rb_obj_as_string (obj=1137889348) at string.c:191
#14 0x43a6f050 in rb_eval (self=1138014008, n=0x0) at eval.c:2895
#15 0x43a6e61f in rb_eval (self=1138014008, n=0x0) at ruby.h:576
#16 0x43a6cb94 in rb_eval (self=1138014008, n=0x0) at eval.c:2028
#17 0x43a6cb94 in rb_eval (self=1138014008, n=0x0) at eval.c:2028
#18 0x43a73141 in rb_call0 (klass=1138029288, recv=1138014008, id=9313, argc=0, argv=0xbffff0e4, body=0x43d4bbd4, nosuper=0) at eval.c:4513
#19 0x43a7361c in rb_call (klass=1138029288, recv=1138014008, mid=9313, argc=1, argv=0xbffff0e0, scope=1) at eval.c:4606
#20 0x43a738ef in rb_funcall (recv=1138014008, mid=9313, n=1) at ruby.h:571
#21 0x43a5df1d in dispatch_message_call() () at messages.cpp:31
#22 0x43a722bd in rb_protect (proc=0x43a5dee4 <dispatch_message_call()>, data=4, state=0xbffff240) at eval.c:4009
#23 0x43a5df55 in MessageEnd() () at messages.cpp:36
#24 0x43772cfe in mm_MessageEnd () from ./cstrike/addons/metamod/dlls/metamod_i386.so

···

On Wed, 14 May 2003 23:19:23 +0200, Jan Bernhardt wrote:

I used to do that, but during experimentation I removed the protection
wrapper. But of course you’re right - I should protect the function call.

OK.

Maybe the ‘message’ variable hasn’t been GC registered correct.
Try swap the line containing rb_global_variable() with the
line containing rb_eval_string(“Message.new”);

void messages_init() { message = rb_eval_string("Message.new"); rb_gc_register_address(&message); args = rb_iv_get(message, "@args"); message_registry = rb_eval_string("MessageRegistry.instance"); }
···

On Thu, 15 May 2003 00:19:23 +0200, Jan Bernhardt wrote:

On Wed, 14 May 2003 23:27:39 +0200, Simon Strandgaard wrote:

you don’t wrap rb_funcall() within a rb_protect()…
Im guessing this is part of the reason.

I used to do that, but during experimentation I removed the protection
wrapper. But of course you’re right - I should protect the function call.


Simon Strandgaard

Unfortunately this did not help either.

  • Jan.
···

On Wed, 14 May 2003 23:42:34 +0200, Simon Strandgaard wrote:

I used to do that, but during experimentation I removed the protection
wrapper. But of course you’re right - I should protect the function call.

OK.

Maybe the ‘message’ variable hasn’t been GC registered correct.
Try swap the line containing rb_global_variable() with the
line containing rb_eval_string(“Message.new”);

void messages_init() { message = rb_eval_string("Message.new"); rb_gc_register_address(&message); args = rb_iv_get(message, "@args"); message_registry = rb_eval_string("MessageRegistry.instance"); }

OK… try this :slight_smile:

void messages_init() {
message = rb_eval_string(“Message.new”);
args = rb_iv_get(message, “@args”);
message_registry = rb_eval_string(“MessageRegistry.instance”);
rb_gc_register_address(&message);
rb_gc_register_address(&args);
rb_gc_register_address(&message_registry);
}

···


Simon Strandgaard

Unfortunately, this didn’t help also. And by looking at how the
Global_List and its related function are implemented I would have been
very surprised if that did the trick… Maybe the problem lies somewhere
completely different. But I’m pretty sure that the other code is correct.

  • Jan.
···

On Wed, 14 May 2003 23:47:33 +0200, Simon Strandgaard wrote:

OK… try this :slight_smile:

void messages_init() {
message = rb_eval_string(“Message.new”); args = rb_iv_get(message,
@args”);
message_registry = rb_eval_string(“MessageRegistry.instance”);
rb_gc_register_address(&message);
rb_gc_register_address(&args);
rb_gc_register_address(&message_registry);
}
}


Simon Strandgaard

OK… try this :slight_smile:

void messages_init() {
message = rb_eval_string(“Message.new”);
args = rb_iv_get(message, “@args”);

This is not safe, as in the next line the GC might run and then message
and args would be collected as they haven’t been registered yet.

···

On Thu, May 15, 2003 at 06:48:22AM +0900, Simon Strandgaard wrote:

message_registry = rb_eval_string("MessageRegistry.instance");
rb_gc_register_address(&message);
rb_gc_register_address(&args);
rb_gc_register_address(&message_registry);

}


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

*** PUBLIC flooding detected from erikyyy
THAT’s an erik, pholx… :wink:
– Seen on #LinuxGER

Is it a multithreaded application you are embedding ruby into ?

If it is then you should consider semaphore protect all access to ruby,
because ruby is not thread-safe. Such errors can be hard to solve.

If not… then we will have to dig deeper into the problem :wink:

···

On Thu, 15 May 2003 00:45:00 +0200, Jan Bernhardt wrote:

Unfortunately, this didn’t help also. And by looking at how the
Global_List and its related function are implemented I would have been
very surprised if that did the trick… Maybe the problem lies somewhere
completely different. But I’m pretty sure that the other code is correct.


Simon Strandgaard

Right… too quick - me sorry :slight_smile:

···

On Thu, 15 May 2003 08:17:04 +0900, Mauricio Fernández wrote:

This is not safe, as in the next line the GC might run and then message
and args would be collected as they haven’t been registered yet.


Simon Strandgaard

Half-Life uses a single threaded model and I was afraid that you’d say
that.

  • Jan.
···

On Thu, 15 May 2003 00:06:07 +0200, Simon Strandgaard wrote:

On Thu, 15 May 2003 00:45:00 +0200, Jan Bernhardt wrote:

Unfortunately, this didn’t help also. And by looking at how the
Global_List and its related function are implemented I would have been
very surprised if that did the trick… Maybe the problem lies somewhere
completely different. But I’m pretty sure that the other code is correct.

Is it a multithreaded application you are embedding ruby into ?

If it is then you should consider semaphore protect all access to ruby,
because ruby is not thread-safe. Such errors can be hard to solve.

If not… then we will have to dig deeper into the problem :wink:


Simon Strandgaard

Assuming that no asyncronous call is happening…

Besides the earlier mentioned issues… then your code look great.
So I have absolutely no idea what could be causing these segfaults.

Perhaps the problem is located in your rb_game.cpp file… can you reveal some
more code from this file ?

···

On Thu, 15 May 2003 00:56:35 +0200, Jan Bernhardt wrote:

Half-Life uses a single threaded model


Simon Strandgaard

Sure.

#include <extdll.h>
#include <h_export.h>
#include <meta_api.h>

#include <ruby.h>

#include “mod.h”
#include “rb_player.h”
#include “ruby_engine.h”

void dispatch_server_command();

/**

···

On Thu, 15 May 2003 00:25:56 +0200, Simon Strandgaard wrote:

Besides the earlier mentioned issues… then your code look great.
So I have absolutely no idea what could be causing these segfaults.

Perhaps the problem is located in your rb_game.cpp file… can you reveal some
more code from this file ?

*/
VALUE rb_game_module;

/**
*
*/
VALUE rb_game_initialize(VALUE self) {
return self;
}

/**

  • Writes a log message to the main engine log.
    */
    VALUE rb_game_log(VALUE self, VALUE message) {
    UTIL_LogPrintf(“[%s] %s\n”, MODULE_TAG, STR2CSTR(rb_obj_as_string(message)));
    return Qnil;
    }

/**

  • Registers a server command with the game engine and the Ruby CommandRegistry.
    */
    VALUE rb_game_register_server_command(VALUE self, VALUE name, VALUE target, VALUE method) {
    VALUE command_registry = rb_eval_string(“CommandRegistry.instance”);
    rb_funcall(command_registry, rb_intern(“register_server_command”), 3, name, target, method);
    REG_SVR_COMMAND(STR2CSTR(name), dispatch_server_command);
    return Qnil;
    }

/**
*
*/
VALUE rb_game_get_cvar(VALUE self, VALUE name) {
return rb_str_new2(CVAR_GET_STRING(STR2CSTR(name)));
}

/**
*
*/
VALUE rb_game_get_cvar_float(VALUE self, VALUE name) {
return rb_float_new(CVAR_GET_FLOAT(STR2CSTR(name)));
}

/**
*
*/
VALUE rb_game_map(VALUE self) {
if (gpGlobals->mapname) {
return rb_str_new2(STRING(gpGlobals->mapname));
} else {
return Qnil;
}
}

/**
*
*/
VALUE rb_game_max_client_count(VALUE self) {
return INT2FIX(gpGlobals->maxClients);
}

/**
*
*/
VALUE rb_game_get_players(VALUE self) {
edict_t *edict = NULL;
VALUE player_array = rb_ary_new();

while(((edict = FIND_ENTITY_BY_CLASSNAME(edict, "player")) != NULL) && ! FNullEnt(edict)) {
rb_ary_push(player_array, rb_player_new(edict));
}
return player_array;

}

/**
*
*/
VALUE rb_game_exec(VALUE self, VALUE command) {
SERVER_COMMAND(STR2CSTR(command));
return Qnil;
}

/**
*
*/
VALUE rb_game_get_message_id(VALUE self, VALUE message_name) {
return INT2FIX(GET_USER_MSG_ID(PLID, STR2CSTR(message_name), NULL));
}

/**
*
*/
VALUE rb_game_get_message_name(VALUE self, VALUE message_id) {
int size = 0;
return rb_str_new2(GET_USER_MSG_NAME(PLID, FIX2LONG(message_id), &size));
}

/**

  • Initializes the native part of the Game class.
    */
    void rb_game_init() {
    rb_game_module = rb_define_module(“Game”);
    rb_define_const(rb_game_module, “Mod”, rb_str_new2(GET_GAME_INFO(PLID, GINFO_NAME)));
    rb_define_const(rb_game_module, “PluginPath”, rb_str_new2(GET_PLUGIN_PATH(PLID)));
    rb_define_method(rb_game_module,
    “initialize”,
    RUBY_METHOD_FUNC(rb_game_initialize),
    0);
    rb_define_module_function(rb_game_module, “log”, RUBY_METHOD_FUNC(rb_game_log), 1);
    rb_define_module_function(rb_game_module,
    “map”,
    RUBY_METHOD_FUNC(rb_game_map),
    0);
    rb_define_module_function(rb_game_module,
    “max_client_count”,
    RUBY_METHOD_FUNC(rb_game_max_client_count),
    0);
    rb_define_module_function(rb_game_module,
    “get_players”,
    RUBY_METHOD_FUNC(rb_game_get_players),
    0);
    rb_define_module_function(rb_game_module,
    “register_server_command”,
    RUBY_METHOD_FUNC(rb_game_register_server_command),
    3);
    rb_define_module_function(rb_game_module,
    “get_cvar”,
    RUBY_METHOD_FUNC(rb_game_get_cvar),
    1);
    rb_define_module_function(rb_game_module,
    “get_cvar_float”,
    RUBY_METHOD_FUNC(rb_game_get_cvar_float),
    1);
    rb_define_module_function(rb_game_module,
    “exec”,
    RUBY_METHOD_FUNC(rb_game_exec),
    1);
    rb_define_module_function(rb_game_module,
    “get_message_id”,
    RUBY_METHOD_FUNC(rb_game_get_message_id),
    1);
    rb_define_module_function(rb_game_module,
    “get_message_name”,
    RUBY_METHOD_FUNC(rb_game_get_message_name),
    1);
    }

VALUE dispatch_server_command_intern() {
VALUE command_registry = rb_eval_string(“CommandRegistry.instance”);
VALUE array = rb_ary_new();
VALUE command_name = rb_str_new2(CMD_ARGV(0));

for (int i = 1; i < CMD_ARGC(); i++) {
rb_ary_push(array, rb_str_new2(CMD_ARGV(i)));
}

return rb_funcall(command_registry, rb_intern("dispatch_server_command"), 2, command_name, array);

}

/**

  • Server command callback.
    */
    void dispatch_server_command() {
    int error = 0;
    rb_protect(dispatch_server_command_intern, Qnil, &error);

    if (error) {
    rb_eval_string(“Game.log $!.to_s”);
    rb_eval_string(“Game.log $:”);
    }
    }

//
// DLL api
//

void dll_game_server_activate_post(edict_t *edict_list, int edict_count, int client_max) {
hrb_init();
RETURN_META(MRES_IGNORED);
}

void dll_game_server_deactivate() {
rb_eval_string(“Game.log ‘Server Deactivate’”);

RETURN_META(MRES_IGNORED);

}

qboolean dll_game_client_connect_post(
edict_t *edict,
const char *pszName,
const char *pszAddress,
char szRejectReason[128])
{
rb_eval_string(“Game.log ‘Player connected.’”);

RETURN_META_VALUE(MRES_IGNORED, TRUE);

}

void dll_game_client_disconnect(edict_t *edict) {
rb_eval_string(“Game.log ‘Player disconnected.’”);

RETURN_META(MRES_IGNORED);

}

void dll_game_client_command(edict_t *edict) {
rb_eval_string(“Game.log ‘Client Command.’”);

RETURN_META(MRES_IGNORED);

}

void eng_game_client_command(edict_t *edict, char *szFmt, …) {
va_list argptr;
static char string[256];
va_start(argptr, szFmt);
vsprintf(string, szFmt, argptr);
va_end(argptr);

// rb_eval_string(“Game.log ‘Engine Client Command.’”);
// rb_funcall(rb_game_module, rb_intern(“log”), 1, rb_str_new2(szFmt));
// rb_funcall(rb_game_module, rb_intern(“log”), 1, rb_str_new2(string));

RETURN_META(MRES_IGNORED);

}

[snip rb_game.cpp]

I have been looking at your code for 30 minutes… just staring… I cannot
spot any buggy constructions. Sorry.

For others which has no overview-of/unfamiliar-with ones code
then bigger ruby-extensions is hard debuggable. Have you looked at SWIG ?
Suggestion: I think you will be able to get more help if you used SWIG.

http://www.swig.org/

···

On Thu, 15 May 2003 01:18:57 +0200, Jan Bernhardt wrote:

On Thu, 15 May 2003 00:25:56 +0200, Simon Strandgaard wrote:

Besides the earlier mentioned issues… then your code look great.
So I have absolutely no idea what could be causing these segfaults.

Perhaps the problem is located in your rb_game.cpp file… can you reveal some
more code from this file ?

Sure.


Simon Strandgaard

Im actual writing a tutorial on how to embed ruby into c++ and use SWIG at
the same time. Download the tar.gz file and
look at the “combining everything” section.

http://metaeditor.sf.net/embed/

There is no makefiles for windows… yet.
reason: I doesn’t own a windows-box.

This should get you started :slight_smile:

···

On Thu, 15 May 2003 01:53:20 +0200, Simon Strandgaard wrote:

Suggestion: I think you will be able to get more help if you used SWIG.

http://www.swig.org/


Simon Strandgaard

[snip rb_game.cpp]

I have been looking at your code for 30 minutes… just staring… I
cannot spot any buggy constructions. Sorry.

Maybe it’s not my code’s fault. I’m getting the impression that Ruby might
have a bug here. The messages’ frequency is quite high - it may well be
that over 100 messages per second hit the dispatch, maybe it’s a timing
problem, but I don’t really think that this is very likely because of the
single threaded nature of the HL engine and the Ruby interpreter.

I also tried different Ruby versions (1.6.7, 1.6.8, 1.8.0preview2) - all
versions resulting in the same error. Maybe someone with a deeper insight
into the Ruby GC can offer some ideas here.

For others which has no overview-of/unfamiliar-with ones code then
bigger ruby-extensions is hard debuggable. Have you looked at SWIG ?
Suggestion: I think you will be able to get more help if you used SWIG.

I know about swig, but I don’t think it has any advantages for my purpose.
The wrapping of the engine internals is very straightforward and there
aren’t that many natives. As I did dive that deep into swig I fear that
there is the possibility of losing performance to the genericity (sp?) of
swig.

  • Jan.
···

On Thu, 15 May 2003 00:53:20 +0200, Simon Strandgaard wrote:

Im actual writing a tutorial on how to embed ruby into c++ and use SWIG at
the same time. Download the tar.gz file and
look at the “combining everything” section.

ruby embedded into c++

There is no makefiles for windows… yet.
reason: I doesn’t own a windows-box.

No problem, as I’m developing on Linux anyways.

This should get you started :slight_smile:

I already skipped through your tutorial, I’ll have a look at it again, but
I think this straightforward approach I use now, does it.

Thank you for your efforts and I think you’re doing a good job with the
tutorial.

  • Jan.
···

On Thu, 15 May 2003 01:17:01 +0200, Simon Strandgaard wrote:

Yes I understand you concern (speed issues).

Consider this: One needs help with some ruby-extension code.
A) He posts his C extension on ruby-talk. Nice code but spanning many
lines.
B) He posts his .i file. Spanning a few lines, fairly clean code.

I think (B) will be easier to understand for the ruby-community and
therefore you might get much better help.

“Minimal speed improvements” versus “the ability to get help”.

I admit that I have never seen any speed-comparisons between a “manualy C
extension” and a “SWIG extension”… I am just guessing here :slight_smile:

···

On Thu, 15 May 2003 02:08:28 +0200, Jan Bernhardt wrote:

On Thu, 15 May 2003 00:53:20 +0200, Simon Strandgaard wrote:

For others which has no overview-of/unfamiliar-with ones code then
bigger ruby-extensions is hard debuggable. Have you looked at SWIG ?
Suggestion: I think you will be able to get more help if you used SWIG.

I know about swig, but I don’t think it has any advantages for my purpose.
The wrapping of the engine internals is very straightforward and there
aren’t that many natives. As I did dive that deep into swig I fear that
there is the possibility of losing performance to the genericity (sp?) of
swig.


Simon Strandgaard

I saw the line “#include <extdll.h>” and just assumed DLL == windows :slight_smile:

Are you on-the-fly loading the ruby library ?

I have no experience with this yet… is it straight ahead ?

‘goto bed’, replies might be delayed some hours.

···

On Thu, 15 May 2003 02:12:46 +0200, Jan Bernhardt wrote:

There is no makefiles for windows… yet.
reason: I doesn’t own a windows-box.

No problem, as I’m developing on Linux anyways.


Simon Strandgaard