#include #include #include #include #include #include #include "gm-world.h" #include "gm-app.h" #include "mcp/gm-mcp-session.h" #include "gm-triggers.h" #include "gm-marshal.h" #include "gm-net.h" #include "gm-bogus.h" #include "gm-support.h" #include "gm-debug.h" #define GM_WORLD_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \ GM_TYPE_WORLD, GmWorldPrivate)) void gm_world_save_input_history(GmWorld *world); void gm_world_load_input_history(GmWorld *world); void gm_world_load_triggers(GmWorld *world); void on_gm_world_net_state_changing(GmNet *net, GmNetState state, GmWorld *world); void on_gm_world_net_state_changed(GmNet *net, GmNetState state, GmWorld *world); void on_gm_world_net_net_error(GmNet *net, gchar *error, gint code, GmWorld *world); void on_gm_world_net_bytes_recv(GmNet *net, gchar *text, gint len, GmWorld *world); void on_gm_world_options_option_changed(GmOptions *options, gchar *key, GmWorld *world); void on_gm_world_editor_save(GmEditor *editor, GmWorld *world); struct _GmWorldPrivate { gchar *path; gboolean loaded; gboolean active; guint activity; gchar *buffer; GmOptions *options; GmTriggers *triggers; GmNet *net; GmMcpSession *mcp; GList *history; GList *editors; GmEditingInfo editing_info; gint last_day; gint fd_log; }; /* Signals */ enum { ACTIVATE_REQUEST, LOAD, UNLOAD, STATE_CHANGING, WORLD_ERROR, TEXT_RECEIVED, EDITOR_ADDED, EDITOR_REMOVED, NAME_CHANGED, ACTIVE_CHANGED, ACTIVITY_CHANGED, NUM_SIGNALS }; static guint world_signals[NUM_SIGNALS] = {0}; G_DEFINE_TYPE(GmWorld, gm_world, G_TYPE_OBJECT) static void gm_world_finalize(GObject *object) { GmWorld *world = GM_WORLD(object); gchar *tmp_dir; if (world->priv->path) { gm_options_save(world->priv->options); gm_world_save_input_history(world); gm_triggers_save(world->priv->triggers); // Removing all tmp files tmp_dir = g_strconcat(world->priv->path, "/tmp", NULL); gm_directory_remove_all(tmp_dir, FALSE); g_free(tmp_dir); } if (world->priv->fd_log > 0) { close(world->priv->fd_log); } gm_g_list_free_simple(world->priv->history); g_free(world->priv->path); g_free(world->priv->buffer); g_free(world->priv->editing_info.name); g_free(world->priv->editing_info.upload); g_list_free(world->priv->editing_info.lines); g_object_unref(world->priv->triggers); g_object_unref(world->priv->options); g_object_unref(world->priv->net); g_object_unref(world->priv->mcp); G_OBJECT_CLASS(gm_world_parent_class)->finalize(object); } static void gm_world_class_init(GmWorldClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = gm_world_finalize; world_signals[ACTIVATE_REQUEST] = g_signal_new("activate_request", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, activate_request), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); world_signals[LOAD] = g_signal_new("load", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, load), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); world_signals[UNLOAD] = g_signal_new("unload", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, unload), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); world_signals[STATE_CHANGING] = g_signal_new("state_changing", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, state_changing), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); world_signals[WORLD_ERROR] = g_signal_new("world_error", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, world_error), NULL, NULL, gm_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); world_signals[TEXT_RECEIVED] = g_signal_new("text_received", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, text_received), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); world_signals[EDITOR_ADDED] = g_signal_new("editor_added", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, editor_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); world_signals[EDITOR_REMOVED] = g_signal_new("editor_removed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, editor_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); world_signals[NAME_CHANGED] = g_signal_new("name_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, name_changed), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); world_signals[ACTIVE_CHANGED] = g_signal_new("active_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, active_changed), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); world_signals[ACTIVITY_CHANGED] = g_signal_new("activity_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmWorldClass, activity_changed), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); g_type_class_add_private(object_class, sizeof(GmWorldPrivate)); } void gm_world_create_default_settings(GmWorld *world) { const gchar *loc = gm_default_charset(); world->priv->options = gm_options_new(); gm_options_set(world->priv->options, "name", ""); gm_options_set(world->priv->options, "autoload", "0"); gm_options_set(world->priv->options, "host", ""); gm_options_set(world->priv->options, "port", "1111"); gm_options_set(world->priv->options, "player_name", ""); gm_options_set(world->priv->options, "reconnect", "0"); gm_options_set(world->priv->options, "password", ""); gm_options_set(world->priv->options, "charset", (gchar *)loc); gm_options_set(world->priv->options, "history_length", "500"); // Non configurable options gm_options_set(world->priv->options, "pane_position", "150"); } static void gm_world_init(GmWorld *world) { world->priv = GM_WORLD_GET_PRIVATE(world); gm_world_create_default_settings(world); world->priv->path = NULL; world->priv->loaded = FALSE; world->priv->history = NULL; world->priv->activity = 0; world->priv->triggers = gm_triggers_new(); world->priv->net = gm_net_new(); world->priv->mcp = gm_mcp_session_new(G_OBJECT(world)); world->priv->buffer = NULL; world->priv->editing_info.is_editing = FALSE; g_signal_connect(world->priv->net, "state_changing", G_CALLBACK(on_gm_world_net_state_changing), world); g_signal_connect(world->priv->net, "state_changed", G_CALLBACK(on_gm_world_net_state_changed), world); g_signal_connect(world->priv->net, "net_error", G_CALLBACK(on_gm_world_net_net_error), world); g_signal_connect(world->priv->net, "bytes_recv", G_CALLBACK(on_gm_world_net_bytes_recv), world); g_signal_connect(world->priv->options, "option_changed", G_CALLBACK(on_gm_world_options_option_changed), world); } void gm_world_load_input_history(GmWorld *world) { FILE *f; gchar line[1024], *filename; GString *str; filename = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "history", NULL); gm_debug_msg(DEBUG_DEFAULT, "GmWorld.LoadInputHistory: loading history (%s)!", filename); if ((f = fopen(filename, "r")) != NULL) { str = g_string_new(""); while (fgets(line, 1024 - 1, f) != NULL) { g_string_append(str, line); if (line[strlen(line) - 1] == '\n') { if (line[1] != '\0') { // Empty lines, we don't need to process those world->priv->history = g_list_append(world->priv->history, g_strndup(str->str, strlen(str->str) - 1)); } g_string_erase(str, 0, -1); } } g_string_free(str, TRUE); fclose(f); } else { gm_debug_msg(DEBUG_DEFAULT, "GmWorld.LoadInputHistory: could not retrieve contents of " "file %s (%s)", filename, strerror(errno)); } g_free(filename); } void gm_world_save_input_history(GmWorld *world) { FILE *f; gchar *filename; GList *elem; if (world->priv->path == NULL) { return; } filename = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "history", NULL); f = fopen(filename, "w"); if (f) { gm_debug_msg(DEBUG_DEFAULT, "GmWorld.SaveInputHistory: saving input history to %s", filename); for (elem = world->priv->history; elem; elem = elem->next) { fprintf(f, "%s\n", (gchar *) (elem->data)); } fclose(f); chmod(filename, 0660); } else { gm_debug_msg(DEBUG_DEFAULT, "GmWorld.SaveInputHistory: couldn't open history file (%s)" " for saving: %s", filename, strerror(errno)); } g_free(filename); } void gm_world_load_triggers(GmWorld *world) { gchar *path; if (world->priv->triggers) { g_object_unref(world->priv->triggers); } path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "triggers", NULL); world->priv->triggers = gm_triggers_new_from_file(path); g_free(path); } /* Public */ GmWorld * gm_world_new(gchar *path) { GmWorld *world = GM_WORLD(g_object_new(GM_TYPE_WORLD, NULL)); gchar *options_path; if (path != NULL) { options_path = g_strconcat(path, "/settings", NULL); gm_debug_msg(DEBUG_DEFAULT, "GmWorld.new: creating new world for %s", path); world->priv->path = g_strdup(path); gm_debug_msg(DEBUG_DEFAULT, "GmWorld.new: creating default world settings for %s", path); gm_options_load(world->priv->options, options_path); if (strlen(gm_options_get(world->priv->options, "charset")) == 0) { gm_options_set(world->priv->options, "charset", gm_default_charset()); } g_free(options_path); gm_world_load_input_history(world); gm_world_load_triggers(world); } /* CHECK: all done? */ return world; } GmWorld * gm_world_dup(GmWorld *source) { GmWorld *copy = gm_world_new(NULL); g_object_unref(copy->priv->options); copy->priv->options = gm_options_dup(source->priv->options); copy->priv->triggers = gm_triggers_dup(source->priv->triggers); return copy; } void gm_world_load(GmWorld *world) { if (world->priv->loaded) { g_signal_emit(world, world_signals[ACTIVATE_REQUEST], 0); return; } world->priv->loaded = TRUE; g_signal_emit(world, world_signals[LOAD], 0); g_signal_emit(world, world_signals[ACTIVATE_REQUEST], 0); gm_world_connect(world); } void gm_world_unload(GmWorld *world) { //GList *w; if (world->priv->loaded) { world->priv->loaded = FALSE; gm_world_disconnect(world); while (world->priv->editors) { gm_world_remove_editor(world, GM_EDITOR(world->priv->editors->data)); } g_signal_emit(world, world_signals[UNLOAD], 0); } } GmTriggers * gm_world_triggers(GmWorld *world) { return world->priv->triggers; } GmOptions * gm_world_options(GmWorld *world) { return world->priv->options; } const gchar * gm_world_name(GmWorld *world) { return gm_options_get(world->priv->options, "name"); } const gchar * gm_world_path(GmWorld *world) { return world->priv->path; } GList ** gm_world_history(GmWorld *world) { return &(world->priv->history); } gboolean gm_world_loaded(GmWorld *world) { return world->priv->loaded; } gboolean gm_world_connected(GmWorld *world) { return gm_net_state(world->priv->net) == GM_NET_STATE_CONNECTED; } gboolean gm_world_disconnected(GmWorld *world) { return gm_net_state(world->priv->net) == GM_NET_STATE_DISCONNECTED; } GmNetState gm_world_state(GmWorld *world) { return gm_net_state(world->priv->net); } const gchar * gm_world_current_host(GmWorld *world) { return gm_net_current_host(world->priv->net); } const gchar * gm_world_current_port(GmWorld *world) { return gm_net_current_port(world->priv->net); } void gm_world_connect(GmWorld *world) { /*if (strlen(gm_options_get(world->priv->options, "host")) == 0) { g_signal_emit(world, world_signals[WORLD_ERROR], 0, _("World has no host, please fill in a host first")); } else if (strlen(gm_options_get(world->priv->options, "port")) == 0) { g_signal_emit(world, world_signals[WORLD_ERROR], 0, _("World has no port, please fill in a port first")); } else {*/ gm_net_connect(world->priv->net, gm_options_get(world->priv->options, "host"), gm_options_get(world->priv->options, "port")); //} } void gm_world_connect_to(GmWorld *world, gchar *host, gchar *port) { gm_net_connect(world->priv->net, host, port); } void gm_world_disconnect(GmWorld *world) { gm_net_disconnect(world->priv->net); } void gm_world_log(GmWorld *world, GmLogType type, gchar *text) { GString *s; gchar *start, *log, *no_ansi; struct tm *timet; time_t timer; gint len; if (type == LOG_NONE) { return; } timer = time(0); timet = localtime(&timer); if (world->priv->fd_log <= 0 || world->priv->last_day != timet->tm_mday) { if (world->priv->fd_log > 0) { close(world->priv->fd_log); } log = g_strdup_printf("%s/logs/%04d-%02d-%02d.log", world->priv->path, timet->tm_year + 1900, timet->tm_mon + 1, timet->tm_mday); world->priv->fd_log = open(log, O_APPEND | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); g_free(log); world->priv->last_day = timet->tm_mday; } if (world->priv->fd_log == -1) { return; } start = g_strdup_printf("[%02d:%02d:%02d] ", timet->tm_hour, timet->tm_min, timet->tm_sec); s = g_string_new(start); g_free(start); switch (type) { case LOG_IN: s = g_string_append_c(s, '<'); break; case LOG_OUT: s = g_string_append_c(s, '>'); break; case LOG_MCP_IN: s = g_string_append(s, "[MCP] <"); break; case LOG_MCP_OUT: s = g_string_append(s, "[MCP] >"); break; case LOG_STATUS: s = g_string_append_c(s, '#'); break; default: break; } s = g_string_append(s, " "); s = g_string_append(s, text); no_ansi = gm_ansi_strip(g_strdup(s->str)); len = strlen(no_ansi); write(world->priv->fd_log, no_ansi, strlen(no_ansi)); if (no_ansi[len - 1] != '\n') { write(world->priv->fd_log, "\n", 1); } g_free(no_ansi); g_string_free(s, TRUE); } void gm_world_parse_legacy_editing_start(GmWorld *world, gchar *line) { gchar *name_start, *upload_start; GmEditingInfo *einfo = &(world->priv->editing_info); name_start = strstr(line, "name: "); if (!name_start) { return; } upload_start = strstr(line, " upload: "); if (!upload_start) { return; } einfo->is_editing = TRUE; einfo->name = g_strndup(name_start + 6, (upload_start - name_start) - 6); einfo->upload = g_strndup(upload_start + 9, (upload_start - line) + 9); GM_DEBUG("Editing info: %s %s", einfo->name, einfo->upload); einfo->lines = NULL; } void gm_world_process_line(GmWorld *world, gchar *line) { gchar *non_text_start = NULL; GmEditingInfo *einfo = &(world->priv->editing_info); GmEditor *editor; if (strncmp(line, "\x1B[0m", 4) == 0) { non_text_start = g_strdup(line + 4); } else { non_text_start = g_strdup(line); } if (einfo->is_editing) { if (strcmp(non_text_start, ".") == 0) { editor = gm_editor_new(einfo->name, einfo->upload, einfo->lines); g_signal_connect(editor, "save", G_CALLBACK(on_gm_world_editor_save), world); gm_world_add_editor(world, editor); einfo->is_editing = FALSE; g_free(einfo->name); einfo->name = NULL; g_free(einfo->upload); einfo->upload = NULL; g_list_free(einfo->lines); einfo->lines = NULL; } else { einfo->lines = g_list_append(einfo->lines, g_strdup(non_text_start)); } } else if (strncmp(non_text_start, "#$#", 3) == 0) { if (strncasecmp(non_text_start + 3, " edit ", 6) == 0) { gm_world_parse_legacy_editing_start(world, non_text_start + 9); } else { gm_mcp_session_handle_oob(world->priv->mcp, non_text_start + 3); gm_world_log(world, LOG_MCP_IN, non_text_start); } } else { if (!gm_world_active(world)) { gm_world_set_activity(world, world->priv->activity + 1); } if (strncmp(non_text_start, "#$\"", 3) == 0) { if (strlen(non_text_start) != strlen(line)) { g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, "\x1B[0m"); } gm_world_log(world, LOG_IN, non_text_start + 3); g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, non_text_start + 3); g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, "\n"); } else { g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, line); g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, "\n"); gm_world_log(world, LOG_IN, line); } } g_free(non_text_start); } void gm_world_process_input(GmWorld *world, gchar *text) { #ifdef HAVE_RUBY gchar *space, *script, *argstr; gboolean script_ran = FALSE; if (g_utf8_strlen(text, -1) > 1) { if (text[0] == '/' && text[1] != '/') { space = strstr(text, " "); if (space == NULL) { script = g_strdup(text + 1); argstr = g_strdup(""); } else { script = g_strndup(text + 1, (space - text) - 1); argstr = g_strdup(space + 1); } gm_debug_msg(DEBUG_DEFAULT, "GmWorld.ProcessInput: Trying script %s (%s)", script, argstr); script_ran = gm_scripts_run(gm_app_scripts(gm_app_instance()), world, script, argstr); g_free(script); g_free(argstr); if (script_ran) { return; } } else if (text[0] == '/' && text[1] == '/') { text = text + 1; } } #endif gm_world_sendln(world, text); } void gm_world_writeln(GmWorld *world, gchar *text) { gchar *newline = g_strconcat(text, "\n", NULL); g_signal_emit(world, world_signals[TEXT_RECEIVED], 0, newline); g_free(newline); } void gm_world_remove_editor(GmWorld *world, GmEditor *editor) { g_return_if_fail(g_list_find(world->priv->editors, editor) != NULL); world->priv->editors = g_list_remove(world->priv->editors, editor); g_signal_emit(world, world_signals[EDITOR_REMOVED], 0, G_OBJECT(editor)); g_object_unref(editor); } void gm_world_add_editor(GmWorld *world, GmEditor *editor) { world->priv->editors = g_list_append(world->priv->editors, editor); g_signal_connect_swapped(editor, "close", G_CALLBACK(gm_world_remove_editor), world); g_signal_emit(world, world_signals[EDITOR_ADDED], 0, editor); } void gm_world_sendln_log(GmWorld *world, gchar *text, GmLogType logtype) { gchar *normal; // Convert text from utf-8 to the correct locale normal = gm_to_utf8_with_fallback(text, -1, gm_options_get(world->priv->options, "charset"), "?"); if (!normal) { gm_debug_msg(DEBUG_DEFAULT, "GmWorld.SendlnLog: conversion failed!"); normal = g_strdup(text); } if (logtype != LOG_NONE) { gm_world_log(world, logtype, text); } gm_net_send_line(world->priv->net, normal); g_free(normal); } void gm_world_sendln(GmWorld *world, gchar *text) { gm_world_sendln_log(world, text, LOG_OUT); } void gm_world_set_active(GmWorld *world, gboolean active) { world->priv->active = active; g_signal_emit(world, world_signals[ACTIVE_CHANGED], 0, active); if (active) { gm_world_set_activity(world, 0); } } gboolean gm_world_active(GmWorld *world) { return world->priv->active; } void gm_world_set_activity(GmWorld *world, gint activity) { world->priv->activity = activity; g_signal_emit(world, world_signals[ACTIVITY_CHANGED], 0, activity); } gint gm_world_activity(GmWorld *world) { return world->priv->activity; } void gm_world_name_changed(GmWorld *world) { gchar *tmp_path = world->priv->path ? g_strdup(world->priv->path) : NULL; gchar *new_path = g_strconcat(gm_app_worlds_path(gm_app_instance()), "/", gm_options_get(world->priv->options, "name"), NULL); if (tmp_path && strcmp(tmp_path, new_path) == 0) { g_free(new_path); g_free(tmp_path); return; } g_free(world->priv->path); world->priv->path = new_path; if (tmp_path && g_file_test(tmp_path, G_FILE_TEST_EXISTS)) { // Then! rename the old_path to the new_path rename(tmp_path, world->priv->path); } else if (!g_file_test(world->priv->path, G_FILE_TEST_EXISTS)) { // There was no old path, so a new dir should be made mkdir(world->priv->path, 0755); } g_free(tmp_path); tmp_path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "settings", NULL); gm_options_save_as(world->priv->options, tmp_path); g_free(tmp_path); tmp_path = g_strconcat(world->priv->path, G_DIR_SEPARATOR_S, "triggers", NULL); gm_triggers_save_as(world->priv->triggers, tmp_path); g_free(tmp_path); g_signal_emit(world, world_signals[NAME_CHANGED], 0, gm_options_get(world->priv->options, "name")); } GmMcpSession * gm_world_get_mcp_session(GmWorld *world) { return world->priv->mcp; } void gm_world_auto_login(GmWorld *world) { gchar const *player_name = gm_options_get(world->priv->options, "player_name"); gchar const *passwd = gm_options_get(world->priv->options, "password"); gchar *sendln; if (player_name && *player_name != '\0') { sendln = g_strconcat("connect ", player_name, " ", passwd, NULL); gm_world_sendln_log(world, sendln, LOG_NONE); g_free(sendln); } } /* Callbacks */ void on_gm_world_editor_save(GmEditor *editor, GmWorld *world) { GList *line; if (gm_net_state(world->priv->net) == GM_NET_STATE_CONNECTED) { gm_world_sendln(world, gm_editor_upload_cmd(editor)); for (line = gm_editor_lines(editor); line; line = line->next) { gm_world_sendln(world, (gchar *)(line->data)); } gm_world_sendln(world, "."); gm_editor_saved(editor); } else { gm_world_writeln(world, _("# Could not save editor text, world is " "not connected at the moment")); } } void on_gm_world_net_state_changing(GmNet *net, GmNetState state, GmWorld *world) { g_signal_emit(world, world_signals[STATE_CHANGING], 0, state); if (state == GM_NET_STATE_DISCONNECTED) { gm_mcp_session_reset(world->priv->mcp); } } void on_gm_world_net_state_changed(GmNet *net, GmNetState state, GmWorld *world) { if (state == GM_NET_STATE_CONNECTED) { gm_world_auto_login(world); } } void on_gm_world_net_net_error(GmNet *net, gchar *error, gint code, GmWorld *world) { g_signal_emit(world, world_signals[WORLD_ERROR], 0, error, code); } void on_gm_world_net_bytes_recv(GmNet *net, gchar *text, gint len, GmWorld *world) { gchar *all, *utext, *ptr, *line, *p; gint i; utext = gm_to_utf8_with_fallback(text, len, gm_options_get(world->priv->options, "charset"), "?"); if (!utext) { gm_debug_msg(DEBUG_DEFAULT, "GmWorld.NetBytesRecv: conversion failed!"); utext = g_strndup(text, len); } if (world->priv->buffer != NULL) { all = g_strconcat(world->priv->buffer, utext, NULL); g_free(utext); g_free(world->priv->buffer); world->priv->buffer = NULL; } else { all = utext; } // TODO: UTF-8 compliant line = (gchar *)(malloc((strlen(all) * sizeof(gchar)) + 1)); i = 0; p = all; /* Find lines in `all' and process them */ for (ptr = all; *ptr != '\0'; ptr++) { if (*ptr == '\n') { line[i] = '\0'; line[i + 1] = '\0'; gm_world_process_line(world, line); p = ptr + 1; i = 0; } else if (*ptr != '\r') { line[i] = *ptr; i++; } } if (i > 0) { world->priv->buffer = g_strdup(p); } g_free(all); } void on_gm_world_options_option_changed(GmOptions *options, gchar *key, GmWorld *world) { if (strcmp(key, "name") == 0) { gm_world_name_changed(world); } }