aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2016-02-12 21:54:42 -0500
committerFranklin Wei <git@fwei.tk>2016-02-16 20:42:49 -0500
commitb110e7e0c519cc9575f8d224f0f75aca0d73946f (patch)
treec3f33326a5e4822f2251e8d7370294096ab2eba4 /src
parenta006044fbcb3355f0fa063720e7c41f4971894a0 (diff)
downloadnetcosm-b110e7e0c519cc9575f8d224f0f75aca0d73946f.zip
netcosm-b110e7e0c519cc9575f8d224f0f75aca0d73946f.tar.gz
netcosm-b110e7e0c519cc9575f8d224f0f75aca0d73946f.tar.bz2
netcosm-b110e7e0c519cc9575f8d224f0f75aca0d73946f.tar.xz
support multiple objects sharing the same name
Diffstat (limited to 'src')
-rw-r--r--src/client.c21
-rw-r--r--src/hash.c264
-rw-r--r--src/hash.h18
-rw-r--r--src/multimap.c351
-rw-r--r--src/multimap.h65
-rw-r--r--src/obj.c49
-rw-r--r--src/obj.h32
-rw-r--r--src/room.c47
-rw-r--r--src/room.h18
-rw-r--r--src/server.c1
-rw-r--r--src/server_reqs.c135
-rw-r--r--src/userdb.c101
-rw-r--r--src/userdb.h10
-rw-r--r--src/util.c33
-rw-r--r--src/util.h5
-rw-r--r--src/world.c22
16 files changed, 977 insertions, 195 deletions
diff --git a/src/client.c b/src/client.c
index 62b9fbe..a90dc4b 100644
--- a/src/client.c
+++ b/src/client.c
@@ -80,7 +80,7 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...)
while(ptr[pos])
{
bool is_newline = (ptr[pos] == '\n');
- if(is_newline || pos >= line_width)
+ if(is_newline || pos >= line_width - 1)
{
if(is_newline || !last_space)
last_space = pos;
@@ -270,9 +270,17 @@ bool poll_requests(void)
size_t datalen = packetlen - 1;
packet[MSG_MAX] = '\0';
- if(packetlen <= 0)
+ /* no data yet */
+ if(packetlen < 0)
goto fail;
+ /* parent closed pipe */
+ if(!packetlen)
+ {
+ debugf("master process died\n");
+ exit(0);
+ }
+
got_cmd = true;
unsigned char cmd = packet[0];
@@ -285,6 +293,7 @@ bool poll_requests(void)
break;
}
case REQ_KICK:
+
{
out((char*)data, datalen);
exit(EXIT_SUCCESS);
@@ -626,7 +635,7 @@ int logout_cb(char **save)
int look_cb(char **save)
{
- char *what = strtok_r(NULL, " ", save);
+ char *what = strtok_r(NULL, "", save);
if(!what)
client_look();
else
@@ -645,7 +654,7 @@ int inventory_cb(char **save)
int take_cb(char **save)
{
- char *what = strtok_r(NULL, " ", save);
+ char *what = strtok_r(NULL, "", save);
client_take(what);
return CMD_OK;
}
@@ -673,7 +682,7 @@ int go_cb(char **save)
int drop_cb(char **save)
{
- char *what = strtok_r(NULL, " ", save);
+ char *what = strtok_r(NULL, "", save);
client_drop(what);
return CMD_OK;
}
@@ -826,6 +835,8 @@ auth:
case CMD_OK:
goto next_cmd;
case CMD_LOGOUT:
+ free(line);
+ free(orig);
goto auth;
case CMD_QUIT:
free(line);
diff --git a/src/hash.c b/src/hash.c
index 23e4bba..70e5d8d 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -18,6 +18,9 @@
#include "hash.h"
+#include "util.h" // for error()
+
+#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -28,6 +31,7 @@ struct hash_node {
};
struct hash_map {
+ char sentinel; /* for avoiding hash/multihash confusion */
unsigned (*hash)(const void *data);
int (*compare)(const void *a, const void *b);
struct hash_node **table;
@@ -38,6 +42,8 @@ struct hash_map {
size_t n_entries;
};
+#define CHECK_SENTINEL(map) do{if(map && ((struct hash_map*)map)->sentinel!=HASH_SENTINEL)error("hash/multimap mixing");}while(0);
+
unsigned hash_djb(const void *ptr)
{
const char *str = ptr;
@@ -51,18 +57,24 @@ unsigned hash_djb(const void *ptr)
return hash;
}
-/* wrapper to supress warnings */
+/* wrappers to suppress warnings */
int compare_strings(const void *a, const void *b)
{
return strcmp(a,b);
}
+int compare_strings_nocase(const void *a, const void *b)
+{
+ return strcasecmp(a,b);
+}
+
void *hash_init(size_t sz, unsigned (*hash_fn)(const void*),
int (*compare_keys)(const void*, const void*))
{
struct hash_map *ret = calloc(sizeof(struct hash_map), 1);
if(!sz)
sz = 1;
+ ret->sentinel = HASH_SENTINEL;
ret->table = calloc(sz, sizeof(struct hash_node*));
ret->table_sz = sz;
ret->hash = hash_fn;
@@ -74,14 +86,22 @@ void *hash_init(size_t sz, unsigned (*hash_fn)(const void*),
void hash_setfreekey_cb(void *ptr, void (*cb)(void *key))
{
- struct hash_map *map = ptr;
- map->free_key = cb;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ map->free_key = cb;
+ }
}
void hash_setfreedata_cb(void *ptr, void (*cb)(void *data))
{
- struct hash_map *map = ptr;
- map->free_data = cb;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ map->free_data = cb;
+ }
}
void hash_free(void *ptr)
@@ -89,6 +109,7 @@ void hash_free(void *ptr)
if(ptr)
{
struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
for(unsigned i = 0; i < map->table_sz; ++i)
{
struct hash_node *node = map->table[i];
@@ -125,6 +146,7 @@ void *hash_iterate(void *map, void **saveptr, void **keyptr)
saved = *saveptr;
saved->map = map;
+ CHECK_SENTINEL(map);
saved->bucket = 0;
saved->node = NULL;
}
@@ -152,55 +174,108 @@ void *hash_iterate(void *map, void **saveptr, void **keyptr)
return NULL;
}
-void *hash_insert(void *ptr, const void *key, const void *data)
+void hash_overwrite(void *ptr, const void *key, const void *data)
{
- struct hash_map *map = ptr;
- unsigned hash = map->hash(key) % map->table_sz;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ unsigned hash = map->hash(key) % map->table_sz;
- struct hash_node *iter = map->table[hash];
- struct hash_node *last = NULL;
+ struct hash_node *iter = map->table[hash];
+ struct hash_node *last = NULL;
- while(iter)
- {
- if(map->compare(key, iter->key) == 0)
- return (void*)(iter->data);
- last = iter;
- iter = iter->next;
+ while(iter)
+ {
+ if(map->compare(key, iter->key) == 0)
+ {
+ if(map->free_data)
+ map->free_data((void*)iter->data);
+ if(map->free_key)
+ map->free_key((void*)iter->key);
+
+ iter->key = key;
+ iter->data = data;
+
+ return;
+ }
+ last = iter;
+ iter = iter->next;
+ }
+
+ /* insert */
+ struct hash_node *new = calloc(sizeof(struct hash_node), 1);
+ new->key = key;
+ new->data = data;
+ new->next = NULL;
+ if(!last)
+ map->table[hash] = new;
+ else
+ last->next = new;
+ ++map->n_entries;
}
+}
- /* insert */
- struct hash_node *new = calloc(sizeof(struct hash_node), 1);
- new->key = key;
- new->data = data;
- new->next = NULL;
- if(!last)
- map->table[hash] = new;
- else
- last->next = new;
- ++map->n_entries;
+void *hash_insert(void *ptr, const void *key, const void *data)
+{
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ unsigned hash = map->hash(key) % map->table_sz;
+
+ struct hash_node *iter = map->table[hash];
+ struct hash_node *last = NULL;
+ while(iter)
+ {
+ if(map->compare(key, iter->key) == 0)
+ return (void*)(iter->data);
+ last = iter;
+ iter = iter->next;
+ }
+
+ /* insert */
+ struct hash_node *new = calloc(sizeof(struct hash_node), 1);
+ new->key = key;
+ new->data = data;
+ new->next = NULL;
+ if(!last)
+ map->table[hash] = new;
+ else
+ last->next = new;
+ ++map->n_entries;
+ /* fall through */
+ }
return NULL;
}
void *hash_lookup(void *ptr, const void *key)
{
- struct hash_map *map = ptr;
- unsigned hash = map->hash(key) % map->table_sz;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ unsigned hash = map->hash(key) % map->table_sz;
- struct hash_node *iter = map->table[hash];
+ struct hash_node *iter = map->table[hash];
- while(iter)
- {
- if(map->compare(key, iter->key) == 0)
- return (void*)(iter->data);
- iter = iter->next;
+ while(iter)
+ {
+ if(map->compare(key, iter->key) == 0)
+ return (void*)(iter->data);
+ iter = iter->next;
+ }
+ /* fall through */
}
+
return NULL;
}
void hash_insert_pairs(void *ptr, const struct hash_pair *pairs,
size_t pairsize, size_t n)
{
+ CHECK_SENTINEL(ptr);
const char *iter = (const char*)pairs;
for(unsigned i = 0; i < n; ++i)
{
@@ -212,83 +287,108 @@ void hash_insert_pairs(void *ptr, const struct hash_pair *pairs,
bool hash_remove(void *ptr, const void *key)
{
- struct hash_map *map = ptr;
- unsigned hash = map->hash(key) % map->table_sz;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ unsigned hash = map->hash(key) % map->table_sz;
- struct hash_node *iter = map->table[hash], *last = NULL;
+ struct hash_node *iter = map->table[hash], *last = NULL;
- while(iter)
- {
- if(map->compare(key, iter->key) == 0)
+ while(iter)
{
- if(last)
- last->next = iter->next;
- else
- map->table[hash] = iter->next;
- if(map->free_key)
- map->free_key((void*)iter->key);
- if(map->free_data)
- map->free_data((void*)iter->data);
- --map->n_entries;
- free(iter);
- return true;
+ if(map->compare(key, iter->key) == 0)
+ {
+ if(last)
+ last->next = iter->next;
+ else
+ map->table[hash] = iter->next;
+ if(map->free_key)
+ map->free_key((void*)iter->key);
+ if(map->free_data)
+ map->free_data((void*)iter->data);
+ --map->n_entries;
+ free(iter);
+ return true;
+ }
+ last = iter;
+ iter = iter->next;
}
- last = iter;
- iter = iter->next;
+ /* fall through */
}
-
return false;
}
void *hash_getkeyptr(void *ptr, const void *key)
{
- struct hash_map *map = ptr;
- unsigned hash = map->hash(key) % map->table_sz;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ unsigned hash = map->hash(key) % map->table_sz;
- struct hash_node *iter = map->table[hash];
+ struct hash_node *iter = map->table[hash];
- while(iter)
- {
- if(map->compare(key, iter->key) == 0)
+ while(iter)
+ {
+ if(map->compare(key, iter->key) == 0)
return (void*)(iter->key);
- iter = iter->next;
+ iter = iter->next;
+ }
+ /* fall through */
}
return NULL;
}
size_t hash_size(void *ptr)
{
- struct hash_map *map = ptr;
- return map->n_entries;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ return map->n_entries;
+ }
+ else
+ return 0;
}
void *hash_dup(void *ptr)
{
- struct hash_map *map = ptr;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
- struct hash_map *ret = calloc(1, sizeof(struct hash_map));
- memcpy(ret, map, sizeof(*ret));
+ struct hash_map *ret = calloc(1, sizeof(struct hash_map));
+ memcpy(ret, map, sizeof(*ret));
- ret->table = calloc(ret->table_sz, sizeof(struct hash_node*));
- ret->n_entries = 0;
+ ret->table = calloc(ret->table_sz, sizeof(struct hash_node*));
+ ret->n_entries = 0;
- void *save;
- while(1)
- {
- void *key;
- void *data = hash_iterate(ptr, &save, &key);
- if(!data)
- break;
- ptr = NULL;
- if(map->dup_data)
- data = map->dup_data(data);
- hash_insert(ret, key, data);
+ void *save = NULL;
+ while(1)
+ {
+ void *key;
+ void *data = hash_iterate(ptr, &save, &key);
+ if(!data)
+ break;
+ ptr = NULL;
+ if(map->dup_data)
+ data = map->dup_data(data);
+ hash_insert(ret, key, data);
+ }
+ return ret;
}
- return ret;
+ else
+ return NULL;
}
void hash_setdupdata_cb(void *ptr, void *(*cb)(void*))
{
- struct hash_map *map = ptr;
- map->dup_data = cb;
+ if(ptr)
+ {
+ struct hash_map *map = ptr;
+ CHECK_SENTINEL(map);
+ map->dup_data = cb;
+ }
}
diff --git a/src/hash.h b/src/hash.h
index 277fd3f..9ea3627 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -16,17 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#pragma once
+
#include <stdbool.h>
#include <stddef.h>
-#pragma once
-
/* simple, generic chained hash map implementation */
+/* no duplicate keys are allowed */
+/* O(n) insertion */
+
+/* for telling containers apart */
+#define HASH_SENTINEL 0x10
+#define MULTIMAP_SENTINEL 0x11
unsigned hash_djb(const void*);
int compare_strings(const void*, const void*);
+int compare_strings_nocase(const void*, const void*);
-void *hash_init(size_t tabsz, unsigned (*hash_fn)(const void*),
+void *hash_init(size_t tabsz, unsigned (*hash_fn)(const void *key),
int (*compare_key)(const void*, const void*));
void hash_setfreedata_cb(void*, void (*cb)(void *data));
@@ -47,6 +54,9 @@ void hash_free(void*);
*/
void *hash_insert(void*, const void *key, const void *data);
+/* overwrite an existing key/value pair with a new one */
+void hash_overwrite(void*, const void *key, const void *data);
+
/* returns NULL if not found */
void *hash_lookup(void*, const void *key);
@@ -83,7 +93,7 @@ void *hash_getkeyptr(void*, const void *key);
#define SIMP_EQUAL(TYPE, NAME) \
int NAME (const void *a, const void *b) \
{ \
- return !(*((const TYPE*)a) == *((const TYPE*)b)); \
+ return memcmp((a), (b), sizeof(TYPE)); \
}
size_t hash_size(void*);
diff --git a/src/multimap.c b/src/multimap.c
new file mode 100644
index 0000000..d609d0f
--- /dev/null
+++ b/src/multimap.c
@@ -0,0 +1,351 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2016 Franklin Wei
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "globals.h"
+
+#include "multimap.h"
+
+#define CHECK_SENTINEL(map) do{if(map && ((struct multimap_t*)map)->sentinel!=MULTIMAP_SENTINEL)error("hash/multimap mixing");}while(0);
+
+/* contains all the pairs using a key */
+struct multimap_node {
+ struct multimap_list *list;
+ size_t n_pairs;
+ struct multimap_t *map;
+};
+
+struct multimap_t {
+ char sentinel;
+ size_t total_pairs;
+ unsigned refcount;
+
+ /* map of key->multimap nodes */
+ void *hash_tab;
+ int (*compare_val)(const void *val_a, const void *val_b);
+
+ void (*free_data)(void*);
+ void (*free_key)(void*);
+ void *(*dup_data)(void*);
+};
+
+static void free_node(void *ptr)
+{
+ struct multimap_node *node = ptr;
+ /* free the list */
+ struct multimap_list *iter = node->list, *next;
+ while(iter)
+ {
+ next = iter->next;
+ if(node->map->free_data)
+ node->map->free_data(iter->val);
+ if(node->map->free_key)
+ node->map->free_key((void*)iter->key);
+ free(iter);
+ iter = next;
+ }
+ free(node);
+}
+
+static void *dup_node(void *ptr)
+{
+ struct multimap_node *node = ptr;
+ struct multimap_node *ret = calloc(1, sizeof(*ret));
+ memcpy(ret, node, sizeof(*ret));
+
+ struct multimap_list *iter = node->list, *last = NULL;
+ while(iter)
+ {
+ struct multimap_list *newpair = calloc(1, sizeof(*newpair));
+ memcpy(newpair, iter, sizeof(*newpair));
+ if(!last)
+ ret->list = iter;
+ else
+ last->next = iter;
+ last = newpair;
+
+ iter = iter->next;
+ }
+
+ return ret;
+}
+
+void *multimap_init(size_t tabsz,
+ unsigned (*hash_fn)(const void *key),
+ int (*compare_key)(const void *key_a, const void *key_b),
+ int (*compare_val)(const void *val_a, const void *val_b))
+{
+ struct multimap_t *ret = calloc(1, sizeof(struct multimap_t));
+ ret->hash_tab = hash_init(tabsz, hash_fn, compare_key);
+ hash_setfreedata_cb(ret->hash_tab, free_node);
+ hash_setdupdata_cb(ret->hash_tab, dup_node);
+ ret->compare_val = compare_val;
+ ret->sentinel = MULTIMAP_SENTINEL;
+ ret->refcount = 1;
+ return ret;
+}
+
+void multimap_free(void *ptr)
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+ if(!(--map->refcount))
+ {
+ hash_free(map->hash_tab);
+ free(map);
+ }
+ }
+}
+
+const struct multimap_list *multimap_lookup(void *ptr, const void *key, size_t *n_pairs)
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+ struct multimap_node *node = hash_lookup(map->hash_tab, key);
+
+ if(!node)
+ {
+ if(n_pairs)
+ *n_pairs = 0;
+ return NULL;
+ }
+
+ if(n_pairs)
+ *n_pairs = node->n_pairs;
+
+ return node->list;
+ }
+ else
+ return NULL;
+}
+
+bool multimap_insert(void *ptr, const void *key, const void *val)
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+
+ CHECK_SENTINEL(map);
+
+ struct multimap_node *node = hash_lookup(map->hash_tab, key);
+ if(!node)
+ {
+ node = calloc(1, sizeof(struct multimap_node));
+
+ node->map = map;
+
+ node->list = calloc(1, sizeof(struct multimap_list));
+ node->list->key = key;
+ node->list->val = (void*)val;
+ node->list->next = NULL;
+
+ node->n_pairs = 1;
+ ++map->total_pairs;
+
+ hash_insert(map->hash_tab, key, node);
+
+ return true;
+ }
+ else
+ {
+ struct multimap_list *new = calloc(1, sizeof(struct multimap_list));
+ new->key = key;
+ new->val = (void*)val;
+ new->next = node->list;
+
+ ++node->n_pairs;
+ ++map->total_pairs;
+
+ node->list = new;
+
+ return false;
+ }
+ }
+ else
+ return false;
+}
+
+size_t multimap_delete(void *ptr, const void *key, const void *val)
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+
+ struct multimap_node *node = hash_lookup(map->hash_tab, key);
+
+ if(!node)
+ return false;
+ /* iterate over the node's pairs and delete */
+ size_t deleted = 0;
+
+ struct multimap_list *last = NULL, *iter = node->list, *next;;
+ while(iter)
+ {
+ next = iter->next;
+ if(!map->compare_val(val, iter->val))
+ {
+ if(last)
+ last->next = iter->next;
+ else
+ node->list = iter->next;
+ free(iter);
+ ++deleted;
+ --node->n_pairs;
+ --map->total_pairs;
+ }
+ else
+ last = iter;
+ iter = next;
+ }
+
+ if(!node->n_pairs)
+ {
+ hash_remove(map->hash_tab, key);
+ }
+
+ return deleted;
+ }
+ else
+ return 0;
+}
+
+size_t multimap_delete_all(void *ptr, const void *key)
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+
+ struct multimap_node *node = hash_lookup(map->hash_tab, key);
+ if(node)
+ {
+ size_t ret = node->n_pairs;
+ map->total_pairs -= ret;
+
+ hash_remove(map->hash_tab, key);
+
+ return ret;
+ }
+ /* fall through */
+ }
+ return 0;
+}
+
+const struct multimap_list *multimap_iterate(void *ptr, void **save, size_t *n_pairs)
+{
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+
+ struct multimap_node *node;
+ if(map)
+ node = hash_iterate(map->hash_tab, save, NULL);
+ else
+ node = hash_iterate(NULL, save, NULL);
+
+ if(node)
+ {
+ if(n_pairs)
+ *n_pairs = node->n_pairs;
+
+ return node->list;
+ }
+ else
+ return NULL;
+}
+
+size_t multimap_size(void *ptr)
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+ return map->total_pairs;
+ }
+ else
+ return 0;
+}
+
+void multimap_setfreedata_cb(void *ptr, void (*cb)(void *ptr))
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+
+ map->free_data = cb;
+ }
+}
+
+void multimap_setdupdata_cb(void *ptr, void *(*cb)(void *ptr))
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+
+ map->dup_data = cb;
+ }
+}
+
+void *multimap_dup(void *ptr)
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+ ++map->refcount;
+
+ return map;
+ }
+ else
+ return NULL;
+}
+
+void *multimap_copy(void *ptr)
+{
+ if(ptr)
+ {
+ struct multimap_t *map = ptr;
+ CHECK_SENTINEL(map);
+
+ struct multimap_t *ret = calloc(1, sizeof(struct multimap_t));
+ memcpy(ret, map, sizeof(*ret));
+
+ ret->hash_tab = hash_dup(map->hash_tab);
+ ret->refcount = 1;
+
+ /* iterate and replace each node's *map pointer */
+ void *map_ptr = ret->hash_tab, *save;
+ while(1)
+ {
+ struct multimap_node *node = hash_iterate(map_ptr, &save, NULL);
+ if(!node)
+ break;
+ map_ptr = NULL;
+ node->map = ret;
+ }
+
+ return ret;
+ }
+ else
+ return NULL;
+}
diff --git a/src/multimap.h b/src/multimap.h
new file mode 100644
index 0000000..8faa829
--- /dev/null
+++ b/src/multimap.h
@@ -0,0 +1,65 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2016 Franklin Wei
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "hash.h"
+
+/* a multimap implemented as a hash of linked lists */
+/* O(1) insertion and lookup */
+/* O(n) deletion by value, O(1) deletion by key */
+
+/* there can be both duplicate keys AND values */
+
+void *multimap_init(size_t tabsz,
+ unsigned (*hash_fn)(const void *key),
+ int (*compare_key)(const void *key_a, const void *key_b),
+ int (*compare_val)(const void *val_a, const void *val_b));
+
+void multimap_free(void*);
+
+struct multimap_list {
+ const void *key;
+ void *val;
+ struct multimap_list *next;
+};
+
+/* returns a linked list of values with the same key with the length in n_pairs */
+const struct multimap_list *multimap_lookup(void *map, const void *key, size_t *n_pairs);
+
+/* returns true if there was no other pair with the same key */
+bool multimap_insert(void *map, const void *key, const void *val);
+
+/* delete the pair(s) with the given key and value, returns # deleted */
+size_t multimap_delete(void *map, const void *key, const void *val);
+
+/* returns # deleted */
+size_t multimap_delete_all(void *map, const void *key);
+
+/* returns a linked list, NOT individual items of a linked list */
+/* set map to NULL after the initial call */
+const struct multimap_list *multimap_iterate(void *map, void **save, size_t *n_pairs);
+
+size_t multimap_size(void *map);
+
+void multimap_setfreedata_cb(void *map, void (*)(void*));
+
+void multimap_free(void *ptr);
+void *multimap_dup(void *ptr);
+void multimap_setdupdata_cb(void *ptr, void *(*cb)(void *ptr));
+void *multimap_copy(void *ptr);
diff --git a/src/obj.c b/src/obj.c
index 7905700..71c534c 100644
--- a/src/obj.c
+++ b/src/obj.c
@@ -24,6 +24,18 @@
/* map of class names -> object classes */
static void *obj_class_map = NULL;
+static obj_id idcounter = 1;
+
+obj_id obj_get_idcounter(void)
+{
+ return idcounter;
+}
+
+void obj_set_idcounter(obj_id c)
+{
+ idcounter = c;
+}
+
struct object_t *obj_new(const char *class_name)
{
if(!obj_class_map)
@@ -44,21 +56,26 @@ struct object_t *obj_new(const char *class_name)
struct object_t *obj = calloc(1, sizeof(struct object_t));
- obj->refcount = 1;
-
obj->class = hash_lookup(obj_class_map, class_name);
+
if(!obj->class)
{
free(obj);
error("unknown object class '%s'", class_name);
}
- else
- return obj;
+
+ obj->id = idcounter++;
+ obj->refcount = 1;
+
+ return obj;
}
void obj_write(int fd, struct object_t *obj)
{
write_string(fd, obj->class->class_name);
+
+ write_uint64(fd, obj->id);
+
write_string(fd, obj->name);
write_bool(fd, obj->list);
@@ -72,6 +89,8 @@ struct object_t *obj_read(int fd)
struct object_t *obj = obj_new(class_name);
free(class_name);
+ obj->id = read_uint64(fd);
+
obj->name = read_string(fd);
obj->list = read_bool(fd);
if(obj->class->hook_deserialize)
@@ -80,22 +99,32 @@ struct object_t *obj_read(int fd)
return obj;
}
+struct object_t *obj_copy(struct object_t *obj)
+{
+ struct object_t *ret = obj_new(obj->class->class_name);
+ ret->name = strdup(obj->name);
+ ret->list = obj->list;
+ ret->userdata = obj->class->hook_dupdata(obj);
+ return ret;
+}
+
struct object_t *obj_dup(struct object_t *obj)
{
- debugf("Adding an object reference.\n");
+ debugf("Adding an object reference for #%lu.\n", obj->id);
++obj->refcount;
return obj;
}
void obj_free(void *ptr)
{
- debugf("Freeing an object reference.\n");
struct object_t *obj = ptr;
--obj->refcount;
+ debugf("Freeing an object reference for #%lu (%s, %d).\n", obj->id, obj->name, obj->refcount);
+
if(!obj->refcount)
{
- debugf("Freeing obj %s\n", obj->name);
+ debugf("Freeing object #%lu\n", obj->id);
if(obj->class->hook_destroy)
obj->class->hook_destroy(obj);
@@ -109,3 +138,9 @@ void obj_shutdown(void)
hash_free(obj_class_map);
obj_class_map = NULL;
}
+
+int obj_compare(const void *a, const void *b)
+{
+ const struct object_t *c = a, *d = b;
+ return !(c->id == d->id);
+}
diff --git a/src/obj.h b/src/obj.h
index ff26399..c591009 100644
--- a/src/obj.h
+++ b/src/obj.h
@@ -54,18 +54,29 @@ struct obj_class_t {
bool (*hook_drop)(struct object_t*, user_t *user);
void (*hook_destroy)(struct object_t*); // free resources
const char* (*hook_desc)(struct object_t*, user_t*); // get object description
+
+ void *(*hook_dupdata)(struct object_t *obj); // duplicate the userdata pointer
};
+typedef uint64_t obj_id;
+
+/* world modules should not instantiate this directly, use obj_new() instead */
+/* also, members marked with 'protected' should not be modified by the world module */
struct object_t {
- char *name; /* no articles: "a", "an", "the", needs to be free()able */
+ obj_id id; // protected
- struct obj_class_t *class;
+ /* the object name needs to be freeable with free(), and should
+ * not be modified by the world module after being added to a
+ * room */
+ char *name;
- bool list;
+ struct obj_class_t *class;
- unsigned refcount;
+ bool list; /* whether to list in room view */
void *userdata;
+
+ unsigned refcount; // private
};
/* returns a new object of class 'c' */
@@ -77,12 +88,21 @@ void obj_write(int fd, struct object_t *obj);
/* deserialize an object */
struct object_t *obj_read(int fd);
-/* this used to actually make a duplicate of an object...
- * now it just increments its reference count */
+/* this adds a reference to an object, DOES NOT COPY */
struct object_t *obj_dup(struct object_t *obj);
+/* makes a new object with a new ID, but same data fields as the original */
+struct object_t *obj_copy(struct object_t *obj);
+
/* this only frees the object if its reference count is zero */
void obj_free(void*);
/* shut down the obj_* module */
void obj_shutdown(void);
+
+/* internal use */
+obj_id obj_get_idcounter(void);
+void obj_set_idcounter(obj_id);
+
+/* compare two objects */
+int obj_compare(const void *a, const void *b);
diff --git a/src/room.c b/src/room.c
index 58563a8..9a92588 100644
--- a/src/room.c
+++ b/src/room.c
@@ -19,6 +19,7 @@
#include "globals.h"
#include "hash.h"
+#include "multimap.h"
#include "server.h"
#include "room.h"
#include "userdb.h"
@@ -35,7 +36,10 @@ bool room_user_add(room_id id, struct child_data *child)
if(child->user)
{
/* hash_insert returns NULL on success */
- return !hash_insert(room->users, child->user, child);
+ bool ret = !hash_insert(room->users, child->user, child);
+ if(room->data.hook_enter)
+ room->data.hook_enter(id, child);
+ return ret;
}
else
return false;
@@ -46,7 +50,12 @@ bool room_user_del(room_id id, struct child_data *child)
struct room_t *room = room_get(id);
if(child->user)
- return hash_remove(room->users, child->user);
+ {
+ bool ret = hash_remove(room->users, child->user);
+ if(room->data.hook_leave)
+ room->data.hook_leave(id, child);
+ return ret;
+ }
else
return false;
}
@@ -56,7 +65,7 @@ void room_free(struct room_t *room)
hash_free(room->users);
room->users = NULL;
- hash_free(room->objects);
+ multimap_free(room->objects);
room->objects = NULL;
hash_free(room->verbs);
@@ -68,30 +77,42 @@ void room_free(struct room_t *room)
bool room_obj_add(room_id room, struct object_t *obj)
{
- return !hash_insert(room_get(room)->objects, obj->name, obj);
+ return !multimap_insert(room_get(room)->objects, obj->name, obj);
}
-struct object_t *room_obj_iterate(room_id room, void **save)
+const struct multimap_list *room_obj_iterate(room_id room, void **save, size_t *n_pairs)
{
if(room != ROOM_NONE)
- return hash_iterate(room_get(room)->objects, save, NULL);
+ return multimap_iterate(room_get(room)->objects, save, n_pairs);
else
- return hash_iterate(NULL, save, NULL);
+ return multimap_iterate(NULL, save, n_pairs);
+}
+
+const struct multimap_list *room_obj_get(room_id room, const char *name)
+{
+ return multimap_lookup(room_get(room)->objects, name, NULL);
}
-struct object_t *room_obj_get(room_id room, const char *name)
+const struct multimap_list *room_obj_get_size(room_id room, const char *name, size_t *n_objs)
{
- return hash_lookup(room_get(room)->objects, name);
+ return multimap_lookup(room_get(room)->objects, name, n_objs);
}
size_t room_obj_count(room_id room)
{
- return hash_size(room_get(room)->objects);
+ return multimap_size(room_get(room)->objects);
}
bool room_obj_del(room_id room, const char *name)
{
- return hash_remove(room_get(room)->objects, name);
+ return multimap_delete_all(room_get(room)->objects, name);
+}
+
+bool room_obj_del_id(room_id room, const char *name, obj_id id)
+{
+ struct object_t tmp;
+ tmp.id = id;
+ return multimap_delete(room_get(room)->objects, name, &tmp);
}
#define OBJMAP_SIZE 8
@@ -102,8 +123,8 @@ void room_init_maps(struct room_t *room)
{
room->users = hash_init((userdb_size() / 2) + 1, hash_djb, compare_strings);
- room->objects = hash_init(OBJMAP_SIZE, hash_djb, compare_strings);
- hash_setfreedata_cb(room->objects, obj_free);
+ room->objects = multimap_init(OBJMAP_SIZE, hash_djb, compare_strings_nocase, obj_compare);
+ multimap_setfreedata_cb(room->objects, obj_free);
room->verbs = hash_init(VERBMAP_SZ,
hash_djb,
diff --git a/src/room.h b/src/room.h
index 64cfeae..7ed720e 100644
--- a/src/room.h
+++ b/src/room.h
@@ -57,7 +57,7 @@ struct room_t {
room_id adjacent[NUM_DIRECTIONS];
/* hash maps */
- void *objects; /* object name -> object */
+ void *objects; /* multimap of object name -> object */
void *verbs;
void *users; /* username -> child_data */
};
@@ -66,22 +66,24 @@ struct room_t {
bool room_user_add(room_id id, struct child_data *child);
bool room_user_del(room_id id, struct child_data *child);
-/* on the first call, room should be a valid room id, and *save should
+/* On the first call, room should be a valid room id, and *save should
* point to a void pointer. On subsequent calls, room should be
* ROOM_NONE, and *save should remain unchanged from the previous
- * call */
-struct object_t *room_obj_iterate(room_id room, void **save);
+ * call. This call returns a LINKED LIST of objects with the same
+ * name every time it is called, not individual objects. */
+const struct multimap_list *room_obj_iterate(room_id room, void **save, size_t *n_pairs);
/* new should point to a new object allocated with obj_new(), with
* 'name' properly set */
bool room_obj_add(room_id room, struct object_t *obj);
-/* obj should be all lowercase */
-struct object_t *room_obj_get(room_id room, const char *obj);
-
-size_t room_obj_count(room_id room);
+const struct multimap_list *room_obj_get(room_id room, const char *obj);
+const struct multimap_list *room_obj_get_size(room_id room, const char *name, size_t *n_objs);
bool room_obj_del(room_id room, const char *name);
+bool room_obj_del_id(room_id room, const char *name, obj_id id);
+
+size_t room_obj_count(room_id room);
/* local verbs override global verbs */
bool room_verb_add(room_id room, struct verb_t*);
diff --git a/src/server.c b/src/server.c
index 0b1569d..43afd69 100644
--- a/src/server.c
+++ b/src/server.c
@@ -49,6 +49,7 @@ void __attribute__((noreturn)) error(const char *fmt, ...)
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
perror(buf);
+ abort();
exit(EXIT_FAILURE);
}
diff --git a/src/server_reqs.c b/src/server_reqs.c
index 900ff87..9e1513d 100644
--- a/src/server_reqs.c
+++ b/src/server_reqs.c
@@ -19,6 +19,7 @@
#include "globals.h"
#include "hash.h"
+#include "multimap.h"
#include "server.h"
#include "userdb.h"
#include "world.h"
@@ -156,15 +157,32 @@ static void req_send_desc(unsigned char *data, size_t datalen, struct child_data
room_id id = sender->room;
while(1)
{
- struct object_t *obj = room_obj_iterate(id, &save);
+ debugf("Iterating over object name...\n");
+ size_t n_objs;
+ const struct multimap_list *iter= room_obj_iterate(id, &save, &n_objs);
id = ROOM_NONE;
- if(!obj)
+ if(!iter)
break;
- if(!obj->list)
- continue;
- strlcat(buf, "There is a(n) ", sizeof(buf));
- strlcat(buf, obj->name, sizeof(buf));
- strlcat(buf, " here.\n", sizeof(buf));
+
+ const char *name = iter->key;
+ if(n_objs == 1)
+ {
+ char *article = (is_vowel(name[0])?"an":"a");
+ strlcat(buf, "There is ", sizeof(buf));
+ strlcat(buf, article, sizeof(buf));
+ strlcat(buf, " ", sizeof(buf));
+ strlcat(buf, name, sizeof(buf));
+ strlcat(buf, " here.\n", sizeof(buf));
+ }
+ else
+ {
+ strlcat(buf, "There are ", sizeof(buf));
+ char n[32];
+ snprintf(n, sizeof(n), "%lu ", n_objs);
+ strlcat(buf, n, sizeof(buf));
+ strlcat(buf, name, sizeof(buf));
+ strlcat(buf, "s here.\n", sizeof(buf));
+ }
}
send_packet(sender, REQ_BCASTMSG, buf, strlen(buf));
@@ -174,9 +192,12 @@ static void req_send_roomname(unsigned char *data, size_t datalen, struct child_
{
(void) data; (void) datalen; (void) sender;
struct room_t *room = room_get(sender->room);
- send_packet(sender, REQ_BCASTMSG, room->data.name, strlen(room->data.name));
+ if(room->data.name)
+ {
+ send_packet(sender, REQ_BCASTMSG, room->data.name, strlen(room->data.name));
- send_packet(sender, REQ_PRINTNEWLINE, NULL, 0);
+ send_packet(sender, REQ_PRINTNEWLINE, NULL, 0);
+ }
}
static void child_set_room(struct child_data *child, room_id id)
@@ -268,12 +289,24 @@ static void req_kick_always(unsigned char *data, size_t datalen,
static void req_look_at(unsigned char *data, size_t datalen, struct child_data *sender)
{
(void) datalen;
- struct object_t *obj = room_obj_get(sender->room, (const char*)data);
- if(obj)
+ debugf("Looking at '%s'\n", data);
+ size_t n_objs;
+ const struct multimap_list *iter = room_obj_get_size(sender->room, (const char*)data, &n_objs);
+ if(iter)
{
- const char *desc = obj->class->hook_desc(obj, sender);
- send_packet(sender, REQ_BCASTMSG, (void*)desc, strlen(desc));
- send_packet(sender, REQ_PRINTNEWLINE, NULL, 0);
+ int idx = 1; // index of the object
+ while(iter)
+ {
+ struct object_t *obj = iter->val;
+
+ const char *desc = obj->class->hook_desc(obj, sender);
+ if(n_objs > 1)
+ send_msg(sender, "%d) %s\n", idx++, desc);
+ else
+ send_msg(sender, "%s\n", desc);
+
+ iter = iter->next;
+ }
}
else
{
@@ -284,25 +317,36 @@ static void req_look_at(unsigned char *data, size_t datalen, struct child_data *
static void req_take(unsigned char *data, size_t datalen, struct child_data *sender)
{
(void) datalen;
- struct object_t *obj = room_obj_get(sender->room, (const char*)data);
- if(obj)
+ const struct multimap_list *iter = room_obj_get(sender->room, (const char*)data), *next;
+ if(iter)
{
- if(obj->class->hook_take && !obj->class->hook_take(obj, sender))
+ while(iter)
{
- send_msg(sender, "You can't take that.\n");
- return;
+ next = iter->next;
+ struct object_t *obj = iter->val;
+ if(obj)
+ {
+ if(obj->class->hook_take && !obj->class->hook_take(obj, sender))
+ {
+ send_msg(sender, "You can't take that.\n");
+ return;
+ }
+
+ struct object_t *dup = obj_dup(obj);
+ multimap_insert(userdb_lookup(sender->user)->objects,
+ dup->name, dup);
+
+ send_msg(sender, "Taken.\n");
+ }
+ else
+ break;
+ iter = next;
}
- struct object_t *dup = obj_dup(obj);
- hash_insert(userdb_lookup(sender->user)->objects,
- dup->name, dup);
-
room_obj_del(sender->room, (const char*)data);
world_save(WORLDFILE);
userdb_write(USERFILE);
-
- send_msg(sender, "Taken.\n");
}
else
{
@@ -321,17 +365,37 @@ static void req_inventory(unsigned char *data, size_t datalen, struct child_data
while(1)
{
- struct object_t *obj = hash_iterate(ptr, &save, NULL);
+ size_t n_objs;
+ const struct multimap_list *iter = multimap_iterate(ptr, &save, &n_objs);
- if(!obj)
+ if(!iter)
break;
ptr = NULL;
char buf[MSG_MAX];
- int len = snprintf(buf, sizeof(buf), "A %s\n", obj->name);
+ buf[0] = '\0';
+
+ const char *name = iter->key;
+ if(n_objs == 1)
+ {
+ 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));
+ }
- send_packet(sender, REQ_BCASTMSG, (void*)buf, len);
+ send_packet(sender, REQ_BCASTMSG, buf, strlen(buf));
}
if(ptr)
send_msg(sender, "Nothing!\n");
@@ -343,9 +407,18 @@ static void req_drop(unsigned char *data, size_t datalen, struct child_data *sen
(void) data;
struct userdata_t *user = userdb_lookup(sender->user);
+
if(!user)
return;
- struct object_t *obj = hash_lookup(user->objects, (const char*)data);
+
+ size_t n_objs;
+ const struct multimap_list *list = multimap_lookup(user->objects, (const char*)data, &n_objs);
+ if(n_objs != 1)
+ {
+ send_msg(sender, "FIXME: unimplemented %s.\n", data);
+ return;
+ }
+ struct object_t *obj = list->val;
if(!obj)
{
send_msg(sender, "You don't have that.\n");
@@ -354,7 +427,7 @@ static void req_drop(unsigned char *data, size_t datalen, struct child_data *sen
struct object_t *dup = obj_dup(obj);
room_obj_add(sender->room, dup);
- hash_remove(user->objects, (const char*)data);
+ multimap_delete_all(user->objects, (const char*)data);
send_msg(sender, "Dropped.\n");
diff --git a/src/userdb.c b/src/userdb.c
index b434e54..5fc98e0 100644
--- a/src/userdb.c
+++ b/src/userdb.c
@@ -20,6 +20,7 @@
#include "client.h"
#include "hash.h"
+#include "multimap.h"
#include "server.h"
#include "userdb.h"
@@ -32,7 +33,7 @@ static void free_userdata(void *ptr)
if(data->objects)
{
- hash_free(data->objects);
+ multimap_free(data->objects);
data->objects = NULL;
}
free(data);
@@ -57,7 +58,8 @@ void userdb_init(const char *file)
{
if(read_uint32(fd) != USERDB_MAGIC)
error("bad userdb magic value");
- while(1)
+ size_t n_users = read_size(fd);
+ for(size_t u = 0; u < n_users; ++u)
{
struct userdata_t *data = calloc(1, sizeof(*data));
@@ -71,20 +73,21 @@ void userdb_init(const char *file)
if(read(fd, &n_objects, sizeof(n_objects)) != sizeof(n_objects))
{
free(data);
- break;
+ error("unexpected EOF");
}
- data->objects = hash_init(MIN(8, n_objects),
- hash_djb,
- compare_strings);
+ data->objects = multimap_init(MIN(8, n_objects),
+ hash_djb,
+ compare_strings_nocase,
+ obj_compare);
- hash_setfreedata_cb(data->objects, obj_free);
- hash_setdupdata_cb(data->objects, (void*(*)(void*))obj_dup);
+ multimap_setfreedata_cb(data->objects, obj_free);
+ multimap_setdupdata_cb(data->objects, (void*(*)(void*))obj_dup);
for(unsigned i = 0; i < n_objects; ++i)
{
struct object_t *obj = obj_read(fd);
- hash_insert(data->objects, obj->name, obj);
+ multimap_insert(data->objects, obj->name, obj);
}
hash_insert(map, data->username, data);
@@ -94,10 +97,15 @@ void userdb_init(const char *file)
}
}
-void userdb_write(const char *file)
+bool userdb_write(const char *file)
{
+ debugf("Writing userdb...\n");
+
int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if(fd < 0)
+ return false;
write_uint32(fd, USERDB_MAGIC);
+ write_size(fd, hash_size(map));
void *save, *ptr = map;
while(1)
{
@@ -110,7 +118,7 @@ void userdb_write(const char *file)
size_t n_objects;
if(user->objects)
- n_objects = hash_size(user->objects);
+ n_objects = multimap_size(user->objects);
else
n_objects = 0;
@@ -123,15 +131,26 @@ void userdb_write(const char *file)
void *objptr = user->objects, *objsave;
while(1)
{
- struct object_t *obj = hash_iterate(objptr, &objsave, NULL);
- if(!obj)
+ const struct multimap_list *iter = multimap_iterate(objptr, &objsave, NULL);
+
+ if(!iter)
break;
objptr = NULL;
- obj_write(fd, obj);
+
+ while(iter)
+ {
+ debugf("Writing an object to disk...\n");
+ obj_write(fd, iter->val);
+ iter = iter->next;
+ }
}
}
}
close(fd);
+
+ debugf("Done writing userdb.\n");
+
+ return true;
}
struct userdata_t *userdb_lookup(const char *key)
@@ -149,9 +168,9 @@ bool userdb_remove(const char *key)
return false;
}
-/* returns NULL on success */
-struct userdata_t *userdb_add(struct userdata_t *data)
+bool userdb_add(struct userdata_t *data)
{
+ userdb_dump();
struct userdata_t *new = calloc(1, sizeof(*new)); /* only in C! */
memcpy(new, data, sizeof(*new));
@@ -159,21 +178,50 @@ struct userdata_t *userdb_add(struct userdata_t *data)
struct userdata_t *old = userdb_lookup(new->username);
if(old && old->objects)
- new->objects = hash_dup(old->objects);
+ {
+ new->objects = multimap_dup(old->objects);
+ }
else
- new->objects = hash_init(8, hash_djb, compare_strings);
+ {
+ new->objects = multimap_init(8, hash_djb, compare_strings_nocase, obj_compare);
- struct userdata_t *ret;
+ multimap_setdupdata_cb(new->objects, (void*(*)(void*))obj_dup);
+ multimap_setfreedata_cb(new->objects, obj_free);
+ }
+
+ hash_overwrite(map, new->username, new);
+
+ return userdb_write(db_file);
+}
- if((ret = hash_insert(map, new->username, new))) /* already exists */
+void userdb_dump(void)
+{
+ debugf("*** User Inventories Dump ***\n");
+ void *userptr = map, *usersave = NULL;
+ while(1)
{
- hash_remove(map, new->username);
- ret = hash_insert(map, new->username, new);
- }
+ struct userdata_t *user = hash_iterate(userptr, &usersave, NULL);
+ if(!user)
+ break;
+ userptr = NULL;
+ void *objptr = user->objects, *objsave;
+ debugf("User %s:\n", user->username);
+ while(1)
+ {
+ const struct multimap_list *iter = multimap_iterate(objptr, &objsave, NULL);
- userdb_write(db_file);
+ if(!iter)
+ break;
+ objptr = NULL;
- return ret;
+ while(iter)
+ {
+ struct object_t *obj = iter->val;
+ debugf(" - Obj #%lu class %s: name %s\n", obj->id, obj->class->class_name, obj->name);
+ iter = iter->next;
+ }
+ }
+ }
}
void userdb_shutdown(void)
@@ -216,7 +264,10 @@ struct userdata_t *userdb_request_lookup(const char *name)
{
send_master(REQ_GETUSERDATA, name, strlen(name) + 1);
if(reqdata_type == TYPE_USERDATA)
+ {
+ returned_reqdata.userdata.objects = NULL;
return &returned_reqdata.userdata;
+ }
return NULL;
}
else
diff --git a/src/userdb.h b/src/userdb.h
index 4804df9..d7eb120 100644
--- a/src/userdb.h
+++ b/src/userdb.h
@@ -38,7 +38,7 @@ struct userdata_t {
//room_id room;
time_t last_login;
- void *objects; /* hash of object names -> objects */
+ void *objects; /* multihash of object names -> objects */
};
/* call before using anything else */
@@ -55,16 +55,13 @@ size_t userdb_size(void);
/*
* adds an entry to the DB
* if it already exists, OVERWRITE
- * returns a pointer to the added entry, NULL on failure
- *
- * a DUPLICATE of the entry will be inserted
*/
-struct userdata_t *userdb_add(struct userdata_t*);
+bool userdb_add(struct userdata_t*);
void userdb_shutdown(void);
/* save the DB to disk */
-void userdb_write(const char*);
+bool userdb_write(const char*);
/* *save should be set to NULL on the first run */
struct userdata_t *userdb_iterate(void **save);
@@ -73,3 +70,4 @@ struct userdata_t *userdb_iterate(void **save);
struct userdata_t *userdb_request_lookup(const char *name);
bool userdb_request_add(struct userdata_t *data);
bool userdb_request_remove(const char *name);
+void userdb_dump(void);
diff --git a/src/util.c b/src/util.c
index ffeae79..cf92dc2 100644
--- a/src/util.c
+++ b/src/util.c
@@ -96,10 +96,10 @@ char *read_string(int fd)
error("read_string: EOF");
}
char *ret = malloc(sz + 1);
- if(read(fd, ret, sz) < 0)
+ if((size_t)read(fd, ret, sz) != sz)
{
free(ret);
- return NULL;
+ error("read_string: EOF");
}
ret[sz] = '\0';
return ret;
@@ -133,6 +133,20 @@ void write_uint32(int fd, uint32_t b)
error("write failed");
}
+uint64_t read_uint64(int fd)
+{
+ uint64_t ret;
+ if(read(fd, &ret, sizeof(ret)) != sizeof(ret))
+ error("unexpected EOF");
+ return ret;
+}
+
+void write_uint64(int fd, uint64_t b)
+{
+ if(write(fd, &b, sizeof(b)) != sizeof(b))
+ error("write failed");
+}
+
size_t read_size(int fd)
{
size_t ret;
@@ -146,3 +160,18 @@ void write_size(int fd, size_t b)
if(write(fd, &b, sizeof(b)) != sizeof(b))
error("write failed");
}
+
+bool is_vowel(char c)
+{
+ switch(tolower(c))
+ {
+ case 'a':
+ case 'e':
+ case 'i':
+ case 'o':
+ case 'u':
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/src/util.h b/src/util.h
index 4c4824e..6de6945 100644
--- a/src/util.h
+++ b/src/util.h
@@ -40,5 +40,10 @@ bool read_bool(int fd);
void write_uint32(int fd, uint32_t i);
uint32_t read_uint32(int fd);
+void write_uint64(int fd, uint64_t i);
+uint64_t read_uint64(int fd);
+
void write_size(int fd, size_t);
size_t read_size(int fd);
+
+bool is_vowel(char c);
diff --git a/src/world.c b/src/world.c
index cd46c4d..256aff1 100644
--- a/src/world.c
+++ b/src/world.c
@@ -19,6 +19,7 @@
#include "globals.h"
#include "hash.h"
+#include "multimap.h"
#include "room.h"
#include "world.h"
#include "userdb.h"
@@ -40,6 +41,7 @@ void world_save(const char *fname)
write_uint32(fd, WORLD_MAGIC);
write(fd, &world_sz, sizeof(world_sz));
write_string(fd, world_name);
+ write_uint64(fd, obj_get_idcounter());
/* write all the global verbs */
void *global_verbs = world_verb_map();
@@ -82,11 +84,18 @@ void world_save(const char *fname)
void *save;
while(1)
{
- struct object_t *obj = room_obj_iterate(id, &save);
- if(!obj)
+ const struct multimap_list *iter = room_obj_iterate(id, &save, NULL);
+ if(!iter)
break;
- id = ROOM_NONE;
- obj_write(fd, obj);
+ while(iter)
+ {
+ struct object_t *obj = iter->val;
+ if(!obj)
+ break;
+ id = ROOM_NONE;
+ obj_write(fd, obj);
+ iter = iter->next;
+ }
}
/* and now all the verbs... */
@@ -158,6 +167,8 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz
return false;
}
+ obj_set_idcounter(read_uint64(fd));
+
size_t n_global_verbs = read_size(fd);
for(unsigned i = 0; i < n_global_verbs; ++i)
{
@@ -185,8 +196,7 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz
{
struct object_t *obj = obj_read(fd);
- if(!room_obj_add(i, obj))
- error("duplicate object name in room '%s'", world[i].data.name);
+ room_obj_add(i, obj);
}
/* read room-local verbs */