aboutsummaryrefslogtreecommitdiff
path: root/src/world.c
diff options
context:
space:
mode:
authorFranklin Wei <frankhwei536@gmail.com>2016-01-31 19:53:45 -0500
committerFranklin Wei <frankhwei536@gmail.com>2016-01-31 19:53:45 -0500
commit0730fc3924dd4e04efbe51287d1d69850404d05f (patch)
tree495d79d0dc26e39c9065c6ceb7d16b9a3e76561d /src/world.c
parent8405274a91e3652ee98a423608a8496ead1edc05 (diff)
downloadnetcosm-0.5.0-rc1.zip
netcosm-0.5.0-rc1.tar.gz
netcosm-0.5.0-rc1.tar.bz2
netcosm-0.5.0-rc1.tar.xz
bump version to 0.5.0-rc10.5.0-rc1
* implements objects using reference counts rather than copying * implements both room-local and global verbs * refactors the world_* functions into a separate module * numerous other changes
Diffstat (limited to 'src/world.c')
-rw-r--r--src/world.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/src/world.c b/src/world.c
new file mode 100644
index 0000000..cd46c4d
--- /dev/null
+++ b/src/world.c
@@ -0,0 +1,340 @@
+/*
+ * 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 "hash.h"
+#include "room.h"
+#include "world.h"
+#include "userdb.h"
+
+/* processed world data */
+
+static struct room_t *world;
+static size_t world_sz;
+static char *world_name;
+
+struct room_t *room_get(room_id id)
+{
+ return world + id;
+}
+
+void world_save(const char *fname)
+{
+ int fd = open(fname, O_CREAT | O_WRONLY, 0644);
+ write_uint32(fd, WORLD_MAGIC);
+ write(fd, &world_sz, sizeof(world_sz));
+ write_string(fd, world_name);
+
+ /* write all the global verbs */
+ void *global_verbs = world_verb_map();
+ size_t n_global_verbs = hash_size(global_verbs);
+ write(fd, &n_global_verbs, sizeof(n_global_verbs));
+
+ if(n_global_verbs)
+ {
+ void *save;
+
+ while(1)
+ {
+ struct verb_t *verb = hash_iterate(global_verbs, &save, NULL);
+ if(!verb)
+ break;
+ global_verbs = NULL;
+ verb_write(fd, verb);
+ }
+ }
+
+ for(unsigned i = 0; i < world_sz; ++i)
+ {
+ write_roomid(fd, &world[i].id);
+ /* unique ID never changes */
+ //write_string(fd, world[i].data.uniq_id);
+ write_string(fd, world[i].data.name);
+ write_string(fd, world[i].data.desc);
+ /* adjacency strings not serialized, only adjacent IDs */
+
+ /* callbacks are static, so are not serialized */
+
+ write(fd, world[i].adjacent, sizeof(world[i].adjacent));
+
+ /* now we serialize all the objects in this room */
+
+ size_t n_objects = room_obj_count(i);
+ write(fd, &n_objects, sizeof(n_objects));
+
+ room_id id = i;
+ void *save;
+ while(1)
+ {
+ struct object_t *obj = room_obj_iterate(id, &save);
+ if(!obj)
+ break;
+ id = ROOM_NONE;
+ obj_write(fd, obj);
+ }
+
+ /* and now all the verbs... */
+
+ void *verb_map = room_verb_map(i);
+ size_t n_verbs = hash_size(verb_map);
+ write_size(fd, n_verbs);
+ while(1)
+ {
+ struct verb_t *verb = hash_iterate(verb_map, &save, NULL);
+ if(!verb)
+ break;
+ verb_map = NULL;
+ verb_write(fd, verb);
+ }
+ }
+ close(fd);
+}
+
+void world_free(void)
+{
+ if(world)
+ {
+ if(world_name)
+ free(world_name);
+ for(unsigned i = 0; i < world_sz; ++i)
+ {
+ room_free(world + i);
+ }
+
+ hash_free(world_verb_map());
+
+ free(world);
+ world = NULL;
+ }
+}
+
+/**
+ * Loads a world using data on disk and in memory.
+ *
+ * @param fname world file
+ * @param data world module data
+ * @param data_sz number of rooms in world module
+ */
+bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz, const char *name)
+{
+ int fd = open(fname, O_RDONLY);
+ if(fd < 0)
+ return false;
+
+ if(read_uint32(fd) != WORLD_MAGIC)
+ return false;
+
+ if(world)
+ world_free();
+
+ read(fd, &world_sz, sizeof(world_sz));
+
+ if(world_sz != data_sz)
+ return false;
+
+ world = calloc(world_sz, sizeof(struct room_t));
+
+ world_name = read_string(fd);
+ if(strcmp(name, world_name))
+ {
+ free(world_name);
+ debugf("Incompatible world state.\n");
+ return false;
+ }
+
+ size_t n_global_verbs = read_size(fd);
+ for(unsigned i = 0; i < n_global_verbs; ++i)
+ {
+ struct verb_t *verb = verb_read(fd);
+ if(!world_verb_add(verb))
+ error("read duplicate global verb '%s'", verb->name);
+ }
+
+ for(unsigned i = 0; i < world_sz; ++i)
+ {
+ room_init_maps(world + i);
+
+ world[i].id = read_roomid(fd);
+ memcpy(&world[i].data, data + i, sizeof(struct roomdata_t));
+ world[i].data.name = read_string(fd);
+ world[i].data.desc = read_string(fd);
+ if(read(fd, world[i].adjacent, sizeof(world[i].adjacent)) < 0)
+ return false;
+
+ size_t n_objects;
+ if(read(fd, &n_objects, sizeof(n_objects)) != sizeof(n_objects))
+ error("world file corrupt");
+
+ for(unsigned j = 0; j < n_objects; ++j)
+ {
+ struct object_t *obj = obj_read(fd);
+
+ if(!room_obj_add(i, obj))
+ error("duplicate object name in room '%s'", world[i].data.name);
+ }
+
+ /* read room-local verbs */
+ size_t n_verbs = read_size(fd);
+ for(unsigned j = 0; j < n_verbs; ++j)
+ {
+ struct verb_t *verb = verb_read(fd);
+ if(!room_verb_add(i, verb))
+ error("duplicate verb '%s' in room '%s'", verb->name,
+ world[i].data.name);
+ }
+ }
+
+ close(fd);
+ return true;
+}
+
+static SIMP_HASH(enum direction_t, dir_hash);
+static SIMP_EQUAL(enum direction_t, dir_equal);
+
+/* loads room data (supplied by the world module) into our internal format */
+void world_init(const struct roomdata_t *data, size_t sz, const char *name)
+{
+ debugf("Loading world with %lu rooms.\n", sz);
+ world = calloc(sz, sizeof(struct room_t));
+ world_sz = 0;
+ world_name = strdup(name);
+
+ void *map = hash_init(sz / 2 + 1, hash_djb, compare_strings);
+
+ for(size_t i = 0; i < sz; ++i)
+ {
+ world[i].id = i;
+ memcpy(&world[i].data, &data[i], sizeof(data[i]));
+
+ /* have to strdup these strings so they can be freed later */
+ //world[i].uniq_id = strdup(world[i].uniq_id);
+ world[i].data.name = strdup(world[i].data.name);
+ world[i].data.desc = strdup(world[i].data.desc);
+ debugf("Loading room '%s'\n", world[i].data.uniq_id);
+
+ if(hash_insert(map, world[i].data.uniq_id, world + i))
+ error("Duplicate room ID '%s'", world[i].data.uniq_id);
+
+ for(int dir = 0; dir < NUM_DIRECTIONS; ++dir)
+ {
+ const char *adjacent_room = world[i].data.adjacent[dir];
+ if(adjacent_room)
+ {
+ struct room_t *room = hash_lookup(map, adjacent_room);
+ if(room)
+ world[i].adjacent[dir] = room->id;
+ }
+ }
+ room_init_maps(world + i);
+
+ world_sz = i + 1;
+ }
+
+ /* second pass to fill in missing references */
+ for(size_t i = 0; i < sz; ++i)
+ {
+ for(int dir = 0; dir < NUM_DIRECTIONS; ++dir)
+ {
+ const char *adjacent_room = world[i].data.adjacent[dir];
+ if(adjacent_room)
+ {
+ struct room_t *room = hash_lookup(map, adjacent_room);
+ if(room)
+ world[i].adjacent[dir] = room->id;
+ else
+ error("unknown room '%s' referenced from '%s'",
+ adjacent_room, world[i].data.uniq_id);
+ }
+ else
+ world[i].adjacent[dir] = ROOM_NONE;
+ }
+ }
+
+ struct direction_pair {
+ enum direction_t dir, opp;
+ } pairs[] = {
+ { DIR_N, DIR_S },
+ { DIR_NE, DIR_SW },
+ { DIR_E, DIR_W },
+ { DIR_SE, DIR_NW },
+ { DIR_UP, DIR_DN },
+ { DIR_IN, DIR_OT },
+ };
+
+ void *dir_map = hash_init(ARRAYLEN(pairs) * 2, dir_hash, dir_equal);
+ for(int n = 0; n < 2; ++n)
+ {
+ for(unsigned i = 0; i < ARRAYLEN(pairs); ++i)
+ {
+ if(!n)
+ hash_insert(dir_map, &pairs[i].dir, &pairs[i].opp);
+ else
+ hash_insert(dir_map, &pairs[i].opp, &pairs[i].dir);
+ }
+ }
+
+ /* third pass to call all the init handlers and check accessibility */
+ for(room_id i = 0; i < (int)world_sz; ++i)
+ {
+ if(world[i].data.hook_init)
+ world[i].data.hook_init(world[i].id);
+ /* check that all rooms are accessible */
+ for(enum direction_t j = 0; j < NUM_DIRECTIONS; ++j)
+ {
+ if(world[i].adjacent[j] != ROOM_NONE)
+ {
+ enum direction_t *opp = hash_lookup(dir_map, &j);
+ struct room_t *adj = room_get(world[i].adjacent[j]);
+ if(adj->adjacent[*opp] != i)
+ debugf("WARNING: Rooms '%s' and '%s' are one-way\n",
+ world[i].data.uniq_id, adj->data.uniq_id);
+ }
+ }
+ }
+
+ hash_free(dir_map);
+
+ hash_free(map);
+}
+
+static void *verb_map = NULL;
+
+#define VERBMAP_SZ 32
+
+static void init_map(void)
+{
+ if(!verb_map)
+ {
+ verb_map = hash_init(VERBMAP_SZ, hash_djb, compare_strings);
+ hash_setfreedata_cb(verb_map, verb_free);
+ }
+}
+
+bool world_verb_add(struct verb_t *verb)
+{
+ init_map();
+ debugf("Added global verb %s\n", verb->name);
+ return !hash_insert(verb_map, verb->name, verb);
+}
+
+void *world_verb_map(void)
+{
+ init_map();
+ return verb_map;
+}