diff options
| author | Franklin Wei <git@fwei.tk> | 2016-02-12 21:54:42 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2016-02-16 20:42:49 -0500 |
| commit | b110e7e0c519cc9575f8d224f0f75aca0d73946f (patch) | |
| tree | c3f33326a5e4822f2251e8d7370294096ab2eba4 /src | |
| parent | a006044fbcb3355f0fa063720e7c41f4971894a0 (diff) | |
| download | netcosm-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.c | 21 | ||||
| -rw-r--r-- | src/hash.c | 264 | ||||
| -rw-r--r-- | src/hash.h | 18 | ||||
| -rw-r--r-- | src/multimap.c | 351 | ||||
| -rw-r--r-- | src/multimap.h | 65 | ||||
| -rw-r--r-- | src/obj.c | 49 | ||||
| -rw-r--r-- | src/obj.h | 32 | ||||
| -rw-r--r-- | src/room.c | 47 | ||||
| -rw-r--r-- | src/room.h | 18 | ||||
| -rw-r--r-- | src/server.c | 1 | ||||
| -rw-r--r-- | src/server_reqs.c | 135 | ||||
| -rw-r--r-- | src/userdb.c | 101 | ||||
| -rw-r--r-- | src/userdb.h | 10 | ||||
| -rw-r--r-- | src/util.c | 33 | ||||
| -rw-r--r-- | src/util.h | 5 | ||||
| -rw-r--r-- | src/world.c | 22 |
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); @@ -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; + } } @@ -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); @@ -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); +} @@ -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); @@ -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, @@ -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); @@ -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; + } +} @@ -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 */ |