diff options
| author | Franklin Wei <git@fwei.tk> | 2016-03-28 14:11:22 -0400 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2016-03-31 17:09:36 -0400 |
| commit | ed95a5621ac9c4f5002e68a981f8b24d5caaedf4 (patch) | |
| tree | aee87cf74280e8f9dd29aca88d12a638f37b2493 /src | |
| parent | 13052597a19fd2212efb7f51f19ed73b9f4b6ba4 (diff) | |
| download | netcosm-ed95a5621ac9c4f5002e68a981f8b24d5caaedf4.zip netcosm-ed95a5621ac9c4f5002e68a981f8b24d5caaedf4.tar.gz netcosm-ed95a5621ac9c4f5002e68a981f8b24d5caaedf4.tar.bz2 netcosm-ed95a5621ac9c4f5002e68a981f8b24d5caaedf4.tar.xz | |
kludge things to compile on old linux
Diffstat (limited to 'src')
| -rw-r--r-- | src/auth.c | 5 | ||||
| -rw-r--r-- | src/auth.h | 6 | ||||
| -rw-r--r-- | src/client.c | 70 | ||||
| -rw-r--r-- | src/client.h | 4 | ||||
| -rw-r--r-- | src/client_reqs.c | 14 | ||||
| -rw-r--r-- | src/client_reqs.h | 2 | ||||
| -rw-r--r-- | src/globals.h | 5 | ||||
| -rw-r--r-- | src/hash.c | 1 | ||||
| -rw-r--r-- | src/obj.c | 6 | ||||
| -rw-r--r-- | src/obj.h | 5 | ||||
| -rw-r--r-- | src/room.h | 22 | ||||
| -rw-r--r-- | src/server.c | 48 | ||||
| -rw-r--r-- | src/server.h | 7 | ||||
| -rw-r--r-- | src/server_reqs.c | 56 | ||||
| -rw-r--r-- | src/server_reqs.h | 7 | ||||
| -rw-r--r-- | src/userdb.c | 5 | ||||
| -rw-r--r-- | src/util.c | 90 | ||||
| -rw-r--r-- | src/util.h | 7 | ||||
| -rw-r--r-- | src/verb.h | 4 | ||||
| -rw-r--r-- | src/world.c | 50 | ||||
| -rw-r--r-- | src/world.h | 6 |
21 files changed, 320 insertions, 100 deletions
@@ -193,11 +193,12 @@ struct userdata_t *auth_check(const char *name2, const char *pass2) { debugf("auth module: user %s found\n", name2); - /* hashes are in HEX to avoid the Trucha bug */ + /* hashes are in lowercase hex to avoid the Trucha bug + * but still allow comparison with strcmp() */ char *new_hash_hex = hash_pass_hex(pass, salt); bool success = true; - /* constant-time comparison to a timing attack */ + /* constant-time comparison to hopefully prevent a timing attack */ for(int i = 0; i < AUTH_HASHLEN; ++i) { if(new_hash_hex[i] != hash[i]) @@ -26,12 +26,6 @@ //#define HASH_ITERS 500000 #define HASH_ITERS 1 -struct authinfo_t { - bool success; - const char *user; - int authlevel; -}; - /* makes admin account */ void first_run_setup(void); diff --git a/src/client.c b/src/client.c index a93ae0e..d66836e 100644 --- a/src/client.c +++ b/src/client.c @@ -20,10 +20,12 @@ #include "auth.h" #include "client.h" +#include "client_reqs.h" #include "hash.h" #include "server.h" #include "room.h" #include "telnet.h" +#include "userdb.h" #include "util.h" bool are_admin = false; @@ -100,6 +102,8 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...) if(is_newline) ++ptr; /* skip the newline */ + + /* skip following spaces */ while(*ptr == ' ') ++ptr; last_space = 0; @@ -184,7 +188,7 @@ tryagain: } } -/* still not encrypted, but a bit more secure than echoing the password! */ +/* still not encrypted, but a bit better than echoing the password! */ char *client_read_password(void) { telnet_echo_off(); @@ -194,8 +198,6 @@ char *client_read_password(void) return ret; } -#define WSPACE " \t\r\n" - #define CMD_OK 0 #define CMD_LOGOUT 1 #define CMD_QUIT 2 @@ -206,7 +208,10 @@ int user_cb(char **save) { char *what = strtok_r(NULL, WSPACE, save); if(!what) + { + out("Usage: USER <ADD|DEL|MODIFY|LIST> <ARGS>\n"); return CMD_OK; + } all_upper(what); @@ -304,9 +309,9 @@ int client_cb(char **save) else if(!strcmp(what, "KICK")) { char *pid_s = strtok_r(NULL, WSPACE, save); - all_upper(pid_s); if(pid_s) { + all_upper(pid_s); if(!strcmp(pid_s, "ALL")) { const char *msg = "Kicking everyone...\n"; @@ -336,7 +341,7 @@ int client_cb(char **save) debugf("Success.\n"); } else - out("Usage: CLIENT KICK <PID>\n"); + out("Usage: CLIENT KICK <PID|ALL>\n"); } return CMD_OK; } @@ -402,6 +407,7 @@ int take_cb(char **save) int wait_cb(char **save) { (void) save; + /* debugging */ send_master(REQ_WAIT, NULL, 0); return CMD_OK; } @@ -416,14 +422,61 @@ int go_cb(char **save) client_look(); } else - out("Expected direction after GO.\n"); + out("I don't understand where you want me to go.\n"); return CMD_OK; } int drop_cb(char **save) { char *what = strtok_r(NULL, "", save); - client_drop(what); + if(what) + client_drop(what); + else + out("You must supply an object.\n"); + return CMD_OK; +} + +int chpass_cb(char **save) +{ + out("Enter current password: "); + char *current = client_read_password(); + + struct userdata_t *current_data = auth_check(current_user, current); + + memset(current, 0, strlen(current)); + free(current); + + if(!current_data) + { + out("Password mismatch.\n"); + return CMD_OK; + } + + out("Enter new password: "); + char *pass1 = client_read_password(); + + out("Retype new password: "); + + char *pass2 = client_read_password(); + + if(strcmp(pass1, pass2)) + { + memset(pass1, 0, strlen(pass1)); + memset(pass2, 0, strlen(pass2)); + free(pass1); + free(pass2); + + out("Passwords do not match.\n"); + return CMD_OK; + } + + memset(pass2, 0, strlen(pass2)); + free(pass2); + + auth_user_add(current_user, pass1, current_data->priv); + + memset(pass1, 0, strlen(pass1)); + return CMD_OK; } @@ -442,9 +495,10 @@ static const struct client_cmd { { "LOOK", look_cb, false }, { "INVENTORY", inventory_cb, false }, { "TAKE", take_cb, false }, - { "WAIT", wait_cb, false }, + { "WAIT", wait_cb, true }, { "GO", go_cb, false }, { "DROP", drop_cb, false }, + { "CHPASS", chpass_cb, false }, }; static void *cmd_map = NULL; diff --git a/src/client.h b/src/client.h index 5b5c5ff..223bf86 100644 --- a/src/client.h +++ b/src/client.h @@ -21,10 +21,6 @@ #include "globals.h" -#include "client_reqs.h" -#include "room.h" -#include "userdb.h" - extern int client_fd, to_parent, from_parent; extern bool are_admin; diff --git a/src/client_reqs.c b/src/client_reqs.c index 39a67f9..21174c4 100644 --- a/src/client_reqs.c +++ b/src/client_reqs.c @@ -19,6 +19,7 @@ #include "globals.h" #include "client.h" +#include "client_reqs.h" #include "hash.h" enum reqdata_typespec reqdata_type = TYPE_NONE; @@ -81,9 +82,7 @@ bool poll_requests(void) int status = *((int*)data); reqdata_type = TYPE_BOOLEAN; - returned_reqdata.boolean = status; - if(!status) - out("You cannot go that way.\n"); + returned_reqdata.boolean = (status == 1); break; } case REQ_GETUSERDATA: @@ -266,8 +265,13 @@ void client_look_at(char *obj) void client_take(char *obj) { - all_lower(obj); - send_master(REQ_TAKE, obj, strlen(obj) + 1); + if(obj) + { + all_lower(obj); + send_master(REQ_TAKE, obj, strlen(obj) + 1); + } + else + out("You must supply an object.\n"); } void client_inventory(void) diff --git a/src/client_reqs.h b/src/client_reqs.h index 979da9a..0d2a63c 100644 --- a/src/client_reqs.h +++ b/src/client_reqs.h @@ -24,6 +24,8 @@ #include "server_reqs.h" #include "userdb.h" +enum room_id; + enum reqdata_typespec { TYPE_NONE = 0, TYPE_USERDATA, TYPE_BOOLEAN } reqdata_type; union reqdata_t { diff --git a/src/globals.h b/src/globals.h index 987d382..5a164ba 100644 --- a/src/globals.h +++ b/src/globals.h @@ -22,16 +22,17 @@ #define _GNU_SOURCE #include <ev.h> + #include <openssl/sha.h> #include <openssl/opensslv.h> #include <arpa/inet.h> #include <arpa/telnet.h> #include <assert.h> -#include <bsd/string.h> // for strlcat #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <limits.h> #include <netdb.h> #include <netinet/in.h> @@ -66,7 +67,7 @@ #define WORLD_MAGIC 0x31415926 #define USERDB_MAGIC 0x27182818 #define MAX_FAILURES 3 -#define NETCOSM_VERSION "0.5.0" +#define NETCOSM_VERSION "0.5.1" /* username length */ #define MAX_NAME_LEN 32 @@ -357,6 +357,7 @@ struct hash_export_node hash_get_internal_node(void *ptr, const void *key) } struct hash_export_node ret; + memset(&ret, 0, sizeof(ret)); ret.node = NULL; return ret; } @@ -144,7 +144,7 @@ struct object_t *obj_copy(struct object_t *obj) struct object_t *obj_dup(struct object_t *obj) { - debugf("Adding an object reference to #%lu.\n", obj->id); + debugf("Adding an object reference to #%" PRI_OBJID ".\n", obj->id); ++obj->refcount; return obj; } @@ -154,11 +154,11 @@ void obj_free(void *ptr) struct object_t *obj = ptr; --obj->refcount; - debugf("Freeing an object reference for #%lu (%s, %d).\n", obj->id, obj->name, obj->refcount); + debugf("Freeing an object reference for #%" PRI_OBJID" (%s, %d).\n", obj->id, obj->name, obj->refcount); if(!obj->refcount) { - debugf("Freeing object #%lu\n", obj->id); + debugf("Freeing object #%"PRI_OBJID"\n", obj->id); if(obj->class->hook_destroy) obj->class->hook_destroy(obj); @@ -21,14 +21,13 @@ #include "globals.h" #include "room.h" +#include "server.h" /* Objects belong to an object class. Objects define their object * class through the class name, which is converted to a class ID * internally. */ -typedef struct child_data user_t; - struct object_t; struct obj_class_t { @@ -55,6 +54,8 @@ struct obj_class_t { typedef uint64_t obj_id; +#define PRI_OBJID PRId64 + struct obj_alias_t { char *alias; struct obj_alias_t *next; @@ -20,15 +20,14 @@ #include "globals.h" -#include "obj.h" -#include "verb.h" - /* Our world is an array of rooms, each having a list of objects in - them, as well as actions that can be performed in the room. Objects - are added by hooks in rooms, which are provided by the world - module. */ + * them, as well as actions that can be performed in them + * (verbs). Objects are added by hooks in rooms, which are provided as + * function pointers by the world module. */ -typedef struct child_data user_t; // definition of child_data in server.h +struct child_data; +struct object_t; +struct verb_t; typedef enum room_id { ROOM_NONE = -1 } room_id; @@ -43,6 +42,7 @@ struct roomdata_t { char *name; char *desc; + /* unmutable properties, changes will have no effect */ const char * const adjacent[NUM_DIRECTIONS]; void (* const hook_init)(room_id id); @@ -50,8 +50,12 @@ struct roomdata_t { /* return values indicate permission to enter/leave, * setting to NULL defaults to true. */ - bool (* const hook_enter)(room_id room, user_t *user); - bool (* const hook_leave)(room_id room, user_t *user); + + /* NOTE: struct child_data is aliased as "user_t"!!! */ + + /* return false to deny entering/leaving a room */ + bool (* const hook_enter)(room_id room, struct child_data *user); + bool (* const hook_leave)(room_id room, struct child_data *user); void (* const hook_serialize)(room_id room, int fd); void (* const hook_deserialize)(room_id room, int fd); void (* const hook_destroy)(room_id room); diff --git a/src/server.c b/src/server.c index e42483c..6d90c61 100644 --- a/src/server.c +++ b/src/server.c @@ -21,6 +21,7 @@ #include "client.h" #include "hash.h" #include "server.h" +#include "server_reqs.h" #include "userdb.h" #include "util.h" #include "world.h" @@ -254,10 +255,34 @@ static void new_connection_cb(EV_P_ ev_io *w, int revents) int readpipe[2]; /* child->parent */ int outpipe [2]; /* parent->child */ + /* try several methods to create a packet pipe between the child and master: */ + + /* first try creating a pipe in "packet mode": see pipe(2) */ if(pipe2(readpipe, O_DIRECT) < 0) - error("error creating pipe, need linux kernel >= 3.4"); + { + /* then try a SOCK_SEQPACKET socket pair: see unix(7) */ + if(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, readpipe) < 0) + { + /* if that failed, try a SOCK_DGRAM socket as a last resort */ + if(socketpair(AF_UNIX, SOCK_DGRAM, 0, readpipe) < 0) + error("couldn't create child-master communication pipe"); + else + debugf("WARNING: Using a SOCK_DGRAM socket pair for IPC, performance may be degraded.\n"); + } + else + debugf("Using a SOCK_SEQPACKET socket pair for IPC.\n"); + } + else + debugf("Using a packet-mode pipe for IPC.\n"); + if(pipe2(outpipe, O_DIRECT) < 0) - error("error creating pipe, need linux kernel >= 3.4"); + { + if(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, outpipe) < 0) + { + if(socketpair(AF_UNIX, SOCK_DGRAM, 0, outpipe) < 0) + error("error creating pipe, need linux kernel >= 3.4"); + } + } pid_t pid = fork(); if(pid < 0) @@ -328,6 +353,7 @@ static void init_signals(void) { struct sigaction sa; + /* SIGINT and SIGTERM cause graceful shutdown */ sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGINT); sa.sa_handler = sigint_handler; @@ -337,6 +363,7 @@ static void init_signals(void) if(sigaction(SIGTERM, &sa, NULL) < 0) error("sigaction"); + /* ignore SIGPIPE */ sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sa.sa_flags = 0; @@ -344,7 +371,7 @@ static void init_signals(void) error("sigaction"); /* libev's default SIGCHLD handler exhibits some really strange - * behavior, which we don't like, so we use our own ;) */ + * behavior, which we don't like, so we use our own */ sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGCHLD); sa.sa_handler = sigchld_handler; @@ -366,7 +393,7 @@ static void parse_args(int argc, char *argv[]) switch(c) { case 'h': /* help */ - debugf("FIXME: usage message"); + debugf("Usage: %s [-d PREFIX] [-a <username> <password>]\n", argv[0]); exit(0); case 'a': /* automatic first-run config */ autoconfig = true; @@ -392,7 +419,7 @@ static SIMP_EQUAL(pid_t, pid_equal); static void check_libs(void) { - debugf("*** Starting NetCosm %s (libev %d.%d, %s) ***\n", + debugf("*** NetCosm %s (libev %d.%d, %s) ***\n", NETCOSM_VERSION, EV_VERSION_MAJOR, EV_VERSION_MINOR, OPENSSL_VERSION_TEXT); assert(ev_version_major() == EV_VERSION_MAJOR && ev_version_minor() >= EV_VERSION_MINOR); @@ -404,29 +431,32 @@ int server_main(int argc, char *argv[]) parse_args(argc, argv); - server_socket = server_bind(); - userdb_init(USERFILE); + /* also performs first-time setup: */ check_userfile(); + load_worldfile(); + /* initialize request map */ reqmap_init(); /* save some time after a fork() */ client_init(); - /* this initial size very low to make iteration faster */ + /* this initial size is set very low to make iteration faster */ child_map = hash_init(16, pid_hash, pid_equal); hash_setfreedata_cb(child_map, free_child_data); hash_setfreekey_cb(child_map, free); debugf("Listening on port %d\n", port); + server_socket = server_bind(); + struct ev_loop *loop = ev_default_loop(0); /* we initialize signals after creating the default event loop - * because libev grabs SIGCHLD */ + * because libev grabs SIGCHLD in the process */ init_signals(); ev_io server_watcher; diff --git a/src/server.h b/src/server.h index 95e537a..6d3d029 100644 --- a/src/server.h +++ b/src/server.h @@ -19,12 +19,9 @@ /* You should use #pragma once everywhere. */ #pragma once -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> +#include "globals.h" -#include "server_reqs.h" -#include "room.h" +enum room_id; /* everything the server needs to manage its children */ struct child_data { diff --git a/src/server_reqs.c b/src/server_reqs.c index 4d89ca4..21787f4 100644 --- a/src/server_reqs.c +++ b/src/server_reqs.c @@ -21,10 +21,11 @@ #include "hash.h" #include "multimap.h" #include "server.h" +#include "server_reqs.h" #include "userdb.h" #include "world.h" -/* sends a single packet to a child, virtually guarantees receipt */ +/* sends a single packet to a child, mostly reliable */ static void send_packet(struct child_data *child, unsigned char cmd, const void *data, size_t datalen) { @@ -188,7 +189,7 @@ static void req_send_desc(unsigned char *data, size_t datalen, struct child_data { strlcat(buf, "There are ", sizeof(buf)); char n[32]; - snprintf(n, sizeof(n), "%lu ", n_objs); + snprintf(n, sizeof(n), "%zu ", n_objs); strlcat(buf, n, sizeof(buf)); strlcat(buf, name, sizeof(buf)); strlcat(buf, "s here.\n", sizeof(buf)); @@ -229,18 +230,33 @@ static void req_set_room(unsigned char *data, size_t datalen, struct child_data static void req_move_room(unsigned char *data, size_t datalen, struct child_data *sender) { (void) data; (void) datalen; (void) sender; + + int status = 0; + enum direction_t dir = *((enum direction_t*)data); struct room_t *current = room_get(sender->room); - room_user_del(sender->room, sender); - - /* TODO: checking */ + /* TODO: bounds checking on `dir' */ room_id new = current->adjacent[dir]; - int status = 0; - if(new != ROOM_NONE) + + if(new == ROOM_NONE) { - child_set_room(sender, new); - status = 1; + send_msg(sender, "You cannot go that way.\n"); + } + else + { + struct room_t *new_room = room_get(new); + + if((!new_room->data.hook_enter || + (new_room->data.hook_enter && new_room->data.hook_enter(new, sender))) && + (!current->data.hook_leave || + (current->data.hook_leave && current->data.hook_leave(sender->room, sender)))) + { + room_user_del(sender->room, sender); + + child_set_room(sender, new); + status = 1; + } } send_packet(sender, REQ_MOVE, &status, sizeof(status)); @@ -403,26 +419,8 @@ static void req_inventory(unsigned char *data, size_t datalen, struct child_data struct object_t *obj = iter->val; if(!strcmp(name, obj->name)) { - if(n_objs == 1) - { - if(obj->default_article) - { - char *article = (is_vowel(name[0])?"An":"A"); - strlcat(buf, article, sizeof(buf)); - strlcat(buf, " ", sizeof(buf)); - } - strlcat(buf, name, sizeof(buf)); - strlcat(buf, "\n", sizeof(buf)); - } - else - { - char n[32]; - snprintf(n, sizeof(n), "%lu", n_objs); - strlcat(buf, n, sizeof(buf)); - strlcat(buf, " ", sizeof(buf)); - strlcat(buf, name, sizeof(buf)); - strlcat(buf, "s\n", sizeof(buf)); - } + format_noun(buf, sizeof(buf), name, n_objs, obj->default_article, true); + strlcat(buf, "\n", sizeof(buf)); send_packet(sender, REQ_BCASTMSG, buf, strlen(buf)); } diff --git a/src/server_reqs.h b/src/server_reqs.h index c77c53a..10c1755 100644 --- a/src/server_reqs.h +++ b/src/server_reqs.h @@ -20,10 +20,10 @@ #include "globals.h" -typedef struct child_data user_t; +#include "server.h" /* child<->master commands */ -/* children might not implement all of these */ +/* not all of these are implemented by both parties */ /* meanings might be different for the server and child, see comments */ #define REQ_NOP 0 /* server, child: do nothing (used for acknowledgement) */ #define REQ_BCASTMSG 1 /* server: broadcast text; child: print following text */ @@ -32,7 +32,7 @@ typedef struct child_data user_t; #define REQ_CHANGEUSER 4 /* server: change child login name */ #define REQ_HANG 5 /* <UNIMP> server: loop forever */ #define REQ_KICK 6 /* server: kick PID with message; child: print message, quit */ -#define REQ_WAIT 7 /* server: sleep 10s */ +#define REQ_WAIT 7 /* <DEBUG> server: sleep 10s */ #define REQ_GETROOMDESC 8 /* server: send child room description */ #define REQ_SETROOM 9 /* server: set child room */ #define REQ_MOVE 10 /* server: move child based on direction; child: success or failure */ @@ -50,6 +50,7 @@ typedef struct child_data user_t; #define REQ_DROP 22 /* server: drop user object if allowed */ #define REQ_LISTUSERS 23 /* server: list users in USERFILE */ #define REQ_EXECVERB 24 /* server: execute a verb with its arguments */ +#define REQ_RAWMODE 25 /* child: toggle the child's processing of commands and instead sending input directly to master */ /* child states, sent as an int to the master */ #define STATE_INIT 0 /* initial state */ diff --git a/src/userdb.c b/src/userdb.c index 101b6d0..0a13319 100644 --- a/src/userdb.c +++ b/src/userdb.c @@ -19,9 +19,12 @@ #include "globals.h" #include "client.h" +#include "client_reqs.h" #include "hash.h" #include "multimap.h" +#include "obj.h" #include "server.h" +#include "server_reqs.h" #include "userdb.h" static void *map = NULL; @@ -229,7 +232,7 @@ void userdb_dump(void) while(iter) { struct object_t *obj = iter->val; - debugf(" - Obj #%lu class %s: name %s\n", obj->id, obj->class->class_name, obj->name); + debugf(" - Obj #%"PRI_OBJID" class %s: name %s\n", obj->id, obj->class->class_name, obj->name); iter = iter->next; } } @@ -187,3 +187,93 @@ bool is_vowel(char c) return false; } } + +/* $NetBSD: strlcat.c,v 1.4 2005/05/16 06:55:48 lukem Exp $ */ +/* from NetBSD: strlcat.c,v 1.16 2003/10/27 00:12:42 lukem Exp */ +/* from OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +char *format_noun(char *buf, size_t len, const char *name, size_t count, bool default_article, bool capitalize) +{ + assert(len > 1); + buf[0] = '\0'; + if(count == 1) + { + if(default_article) + { + char *article = capitalize?(is_vowel(name[0])? "An" : "A"):(is_vowel(name[0])? "an" : "a"); + strlcat(buf, article, len); + strlcat(buf, " ", len); + strlcat(buf, name, len); + } + else + { + char tmp[2]; + tmp[0] = toupper(name[0]); + tmp[1] = '\0'; + strlcat(buf, tmp, len); + strlcat(buf, name + 1, len); + } + } + else + { + char n[32]; + snprintf(n, sizeof(n), "%zu", count); + strlcat(buf, n, len); + strlcat(buf, " ", len); + strlcat(buf, name, len); + strlcat(buf, "s", len); + } + + return buf; +} @@ -21,6 +21,8 @@ #include "room.h" +#define WSPACE " \t\r\n" + /* utility functions */ void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...); void __attribute__((format(printf,4,5))) debugf_real(const char*, int, const char*, const char *fmt, ...); @@ -47,3 +49,8 @@ void write_size(int fd, size_t); size_t read_size(int fd); bool is_vowel(char c); + +size_t strlcat(char *dst, const char *src, size_t siz); + +/* formats a noun's name */ +char *format_noun(char *buf, size_t len, const char *name, size_t count, bool default_article, bool capitalize); @@ -21,6 +21,7 @@ #include "globals.h" #include "room.h" +#include "server.h" /* the verb API is modeled after that of obj_*, this allows for * dynamic creation/deletion of verbs, but is also easily @@ -30,9 +31,6 @@ * callbacks. */ -struct child_data; -typedef struct child_data user_t; - struct verb_t; struct verb_class_t { const char *class_name; diff --git a/src/world.c b/src/world.c index a854e49..8c25d7d 100644 --- a/src/world.c +++ b/src/world.c @@ -29,6 +29,9 @@ static struct room_t *world; static size_t world_sz; static char *world_name; +/* map of room names -> rooms */ +static void *world_map = NULL; + struct room_t *room_get(room_id id) { return world + id; @@ -124,8 +127,22 @@ void world_save(const char *fname) world[i].data.hook_serialize(i, fd); } + /* write the object counter so future objects will have sequential ids */ write_uint64(fd, obj_get_idcounter()); + /* now write the map of room names to ids */ + void *ptr = world_map, *save; + for(unsigned int i = 0; i < world_sz; ++i) + { + void *key; + struct room_t *room = hash_iterate(ptr, &save, &key); + if(!room) + break; + ptr = NULL; + write_string(fd, key); + write_roomid(fd, &room->id); + } + close(fd); } @@ -230,6 +247,18 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz obj_set_idcounter(read_uint64(fd)); + /* read in the room name -> room map */ + + world_map = hash_init(world_sz * 2, hash_djb, compare_strings); + + for(unsigned int i = 0; i < world_sz; ++i) + { + const char *key = read_string(fd); + room_id id = read_roomid(fd); + debugf("'%s' -> %d\n", key, id); + hash_insert(world_map, key, world + id); + } + close(fd); return true; } @@ -240,12 +269,12 @@ static SIMP_EQUAL(enum direction_t, dir_equal); /* loads room data (supplied by the world module) into our internal format */ void world_init(const struct roomdata_t *data, size_t sz, const char *name) { - debugf("Loading world with %lu rooms.\n", sz); + debugf("Loading world with %zu rooms.\n", sz); world = calloc(sz, sizeof(struct room_t)); world_sz = 0; world_name = strdup(name); - void *map = hash_init(sz / 2 + 1, hash_djb, compare_strings); + world_map = hash_init(sz * 2, hash_djb, compare_strings); for(size_t i = 0; i < sz; ++i) { @@ -258,7 +287,7 @@ void world_init(const struct roomdata_t *data, size_t sz, const char *name) world[i].data.desc = strdup(world[i].data.desc); debugf("Loading room '%s'\n", world[i].data.uniq_id); - if(hash_insert(map, world[i].data.uniq_id, world + i)) + if(hash_insert(world_map, world[i].data.uniq_id, world + i)) error("Duplicate room ID '%s'", world[i].data.uniq_id); for(int dir = 0; dir < NUM_DIRECTIONS; ++dir) @@ -266,7 +295,7 @@ void world_init(const struct roomdata_t *data, size_t sz, const char *name) const char *adjacent_room = world[i].data.adjacent[dir]; if(adjacent_room) { - struct room_t *room = hash_lookup(map, adjacent_room); + struct room_t *room = hash_lookup(world_map, adjacent_room); if(room) world[i].adjacent[dir] = room->id; } @@ -284,7 +313,7 @@ void world_init(const struct roomdata_t *data, size_t sz, const char *name) const char *adjacent_room = world[i].data.adjacent[dir]; if(adjacent_room) { - struct room_t *room = hash_lookup(map, adjacent_room); + struct room_t *room = hash_lookup(world_map, adjacent_room); if(room) world[i].adjacent[dir] = room->id; else @@ -339,8 +368,6 @@ void world_init(const struct roomdata_t *data, size_t sz, const char *name) } hash_free(dir_map); - - hash_free(map); } static void *verb_map = NULL; @@ -368,3 +395,12 @@ void *world_verb_map(void) init_map(); return verb_map; } + +room_id room_get_id(const char *uniq_id) +{ + struct room_t *room = hash_lookup(world_map, uniq_id); + if(!room) + return ROOM_NONE; + else + return room->id; +} diff --git a/src/world.h b/src/world.h index 5d9ccae..50f9769 100644 --- a/src/world.h +++ b/src/world.h @@ -48,14 +48,16 @@ void world_save(const char *fname); /* loads the world from disk */ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz, const char *world_name); -/** verbs **/ +/** global verbs **/ bool world_verb_add(struct verb_t*); bool world_verb_del(struct verb_t*); -/* gets the map of verbs */ +/* gets the map of global verbs */ void *world_verb_map(void); void world_free(void); /* this goes in world_ and not room_ */ struct room_t *room_get(room_id id); + +room_id room_get_id(const char *uniq_id); |