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 | |
| 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
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | SOURCES | 1 | ||||
| -rw-r--r-- | export/include/world_api.h | 3 | ||||
| -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 | ||||
| -rw-r--r-- | worlds/test.c | 234 |
20 files changed, 1084 insertions, 328 deletions
@@ -33,8 +33,6 @@ on your system in order for things to work. ## Todo List -* Verbs - * Game scripting * NPCs @@ -2,6 +2,7 @@ src/auth.c src/client.c src/hash.c src/main.c +src/multimap.c src/obj.c src/room.c src/server.c diff --git a/export/include/world_api.h b/export/include/world_api.h index f0d0398..d97d9cb 100644 --- a/export/include/world_api.h +++ b/export/include/world_api.h @@ -21,4 +21,7 @@ #include "globals.h" #include "server.h" +#include "hash.h" +#include "multimap.h" +#include "userdb.h" #include "world.h" 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 */ diff --git a/worlds/test.c b/worlds/test.c index edd17ff..eab1b4c 100644 --- a/worlds/test.c +++ b/worlds/test.c @@ -2,104 +2,69 @@ /************ ROOM DEFINITIONS ************/ -static void portal_init(room_id id) +static void deadend_init(room_id id) { - debugf("portal room init.\n"); - struct object_t *new = obj_new("weapon"); - new->name = strdup("sword"); + struct object_t *new = obj_new("/generic"); + new->name = strdup("shovel"); + new->userdata = strdup("It is a normal shovel with a price tag attached that says $19.99."); new->list = true; - new->userdata = malloc(sizeof(double)); - double p = 3.14159265358979323846L; - memcpy(new->userdata, &p, sizeof(p)); room_obj_add(id, new); + new = obj_copy(new); + if(!room_obj_add(id, new)) + error("failed to add obj"); } -static bool no_take(struct object_t *obj, user_t *user) +static void ew_road_init(room_id id) { - (void)obj; (void)user; - return false; -} - -static void road_sign_init(room_id id) -{ - struct object_t *new = obj_new("sign"); - new->name = strdup("sign"); + struct object_t *new = obj_new("/generic/notake"); + new->name = strdup("large boulder"); + new->userdata = strdup("It is just a boulder. It cannot be moved."); new->list = true; - new->userdata = strdup("The sign reads,\n\nThe Great Road\n==============\nAlagart -- 35km"); - room_obj_add(id, new); +} - struct verb_t *verb = verb_new("read"); - verb->name = strdup("read"); - room_verb_add(id, verb); +static void fork_init(room_id id) +{ + struct verb_t *new = verb_new("dig"); + new->name = strdup("dig"); + room_verb_add(id, new); } const struct roomdata_t netcosm_world[] = { { - "portal_room", - "Portal Room", - "You stand in the middle of a stone room. In to the east lies a portal to the world. Above it, there is a sign that reads `Alacron, 238 A.B.A.`.", - { NONE_N, NONE_NE, "world_start", NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, "world_start", NONE_OT }, - portal_init, - NULL, - NULL, - }, - - { - "world_start", - "Beride Town Square", - "You are in the Beride town square. All around you there are people hurrying to get along. To the north stands a statue of the late King Ajax IV, and to the east is the Great Road. There are exits in all four directions.", - { "beride_square_n_statue", NONE_NE, "great_road_1", NONE_SE, "beride_square_s", NONE_SW, "beride_square_w", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - NULL, + "dead_end", + "Dead End", + "You are at a dead end of a dirt road. The road goes to the east. In the distance you can see that it will eventually fork off. The trees here are very tall royal palms, and they are spaced equidistant from each other.", + { NONE_N, NONE_NE, "ew_road", NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + deadend_init, NULL, NULL, }, { - "beride_square_n_statue", - "King Ajax IV Statue", - "Your path is blocked by an enormous bronze statue. A plaque on the pedestal reads,\n\nKing Ajax IV\n\n182 - 238 A.B.A.\n\nTo the south is the Beride Town Square.", - { NONE_N, NONE_NE, NONE_E, NONE_SE, "world_start", NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - NULL, + "ew_road", + "E/W Dirt road", + "You are on the continuation of a dirt road. There are more trees on both sides of you. The road continues to the east and west.", + { NONE_N, NONE_NE, "fork", NONE_SE, NONE_S, NONE_SW, "dead_end", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + ew_road_init, NULL, NULL, }, { - "great_road_1", - "Great Road", - "You are at the start of a long, winding east-west road through the plains. Directly to your west is Beride.", - { NONE_N, NONE_NE, "great_road_2", NONE_SE, NONE_S, NONE_SW, "world_start", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - road_sign_init, + "fork", + "Fork", + "You are at a fork of two passages, one to the northeast, and one to the southeast. The ground here seems very soft. You can also go back west.", + { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, NONE_SW, "ew_road", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, + fork_init, NULL, NULL, }, - { - "great_road_2", - "Great Road", - "You are on a long, winding east-west road through the plains. To your west you can make out a town.", - { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, NONE_SW, "great_road_1", NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - NULL, - NULL, - NULL, - }, - - { - "beride_square_w", - "Bottomless Pit", - "You take a step onto what seems to be solid rock, but your foot unexplicably slips through it, leading you to lose your balance and slip into the bottomless abyss...", - { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, - NULL, - NULL, - NULL, - }, - - { - "beride_square_s", - "Bottomless Pit", - "You take a step onto what seems to be solid rock, but your foot unexplicably slips through it, leading you to lose your balance and slip into the bottomless abyss...", + "senw_road", + "SE/NW road", + "You are on a southeast/northwest road.", { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT }, NULL, NULL, @@ -108,113 +73,120 @@ const struct roomdata_t netcosm_world[] = { }; const size_t netcosm_world_sz = ARRAYLEN(netcosm_world); -const char *netcosm_world_name = "Alacron 0.1"; +const char *netcosm_world_name = "Dunnet 0.1"; /************ OBJECT DEFINITIONS ************/ -const char *shiny(struct object_t *obj, user_t *user) +static void generic_ser(int fd, struct object_t *obj) { - if(user->state == STATE_ADMIN) - { - static char buf[128]; - double *d = obj->userdata; - snprintf(buf, sizeof(buf), "It has %f written on it.", *d); - return buf; - } - else - return "It's kinda shiny."; -} - -static void weap_serialize(int fd, struct object_t *obj) -{ - if(obj->userdata) - write(fd, obj->userdata, sizeof(double)); + write_string(fd, obj->userdata); } -static void weap_deserialize(int fd, struct object_t *obj) +static void generic_deser(int fd, struct object_t *obj) { - obj->userdata = malloc(sizeof(double)); - read(fd, obj->userdata, sizeof(double)); + obj->userdata = read_string(fd); } -static void weap_destroy(struct object_t *obj) +static void generic_destroy(struct object_t *obj) { free(obj->userdata); - obj->userdata = NULL; } -static void sign_write(int fd, struct object_t *obj) +static const char *generic_desc(struct object_t *obj, user_t *user) { - write_string(fd, obj->userdata); -} - -static void sign_read(int fd, struct object_t *obj) -{ - obj->userdata = read_string(fd); + (void) user; + return obj->userdata; } -static void sign_free(struct object_t *obj) +static void *generic_dup(struct object_t *obj) { - free(obj->userdata); + return strdup(obj->userdata); } -static const char *sign_desc(struct object_t *obj, user_t *user) +static bool no_take(struct object_t *obj, user_t *user) { - (void) user; - return obj->userdata; + (void) obj; (void) user; + return false; } const struct obj_class_t netcosm_obj_classes[] = { + /* a generic, takable object class with userdata as its description */ { - "weapon", - weap_serialize, - weap_deserialize, + "/generic", + generic_ser, + generic_deser, NULL, NULL, - weap_destroy, - shiny + generic_destroy, + generic_desc, + generic_dup, }, + /* a generic, non-takable object class */ { - "sign", - sign_write, // serialize - sign_read, // deserialize - no_take, // take - NULL, // drop - sign_free, // destroy - sign_desc - }, + "/generic/notake", + generic_ser, + generic_deser, + no_take, + NULL, + generic_destroy, + generic_desc, + generic_dup, + } }; const size_t netcosm_obj_classes_sz = ARRAYLEN(netcosm_obj_classes); /**************** VERB DEFINITIONS ****************/ -static void read_exec(struct verb_t *verb, char *args, user_t *user) +static void dig_exec(struct verb_t *verb, char *args, user_t *user) { (void) verb; - char *save; - if(!args) + (void) args; + if(!multimap_lookup(userdb_lookup(user->user)->objects, "shovel", NULL)) { - send_msg(user, "Read what?\n"); + send_msg(user, "You have nothing with which to dig.\n"); return; } - char *what = strtok_r(args, " ", &save); - struct object_t *obj = room_obj_get(user->room, what); - if(obj) + + if(!strcmp(room_get(user->room)->data.name, "Fork")) { - if(!strcmp(obj->class->class_name, "sign")) - send_msg(user, "%s\n", obj->class->hook_desc(obj, user)); + if(!room_obj_get(user->room, "cpu")) + { + struct object_t *new = obj_new("/generic"); + new->name = strdup("CPU card"); + new->userdata = strdup("The CPU board has a VAX chip on it. It seems to have 2 Megabytes of RAM onboard."); + new->list = true; + room_obj_add(user->room, new); + send_msg(user, "I think you found something.\n"); + } else - send_msg(user, "You can't read that.\n"); + { + goto nothing; + } } - else - send_msg(user, "I don't know what that is.\n"); + + return; + +nothing: + send_msg(user, "Digging here reveals nothing.\n"); } const struct verb_class_t netcosm_verb_classes[] = { - { "read", - read_exec }, + { "dig", + dig_exec }, + /* + { "shake", + shake_exec }, + { "climb", + climb_exec }, + { "put", + put_exec }, + { "eat", + eat_exec }, + { "feed", + feed_exec }, + */ }; const size_t netcosm_verb_classes_sz = ARRAYLEN(netcosm_verb_classes); |