00001
00013 #include <assert.h>
00014 #include <stddef.h>
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <string.h>
00018 #include "boris.h"
00019
00020
00021
00022
00023
00027 struct plugin_character_class {
00028 struct plugin_basic_class base_class;
00029 struct plugin_character_interface character_interface;
00030 };
00031
00032 struct character {
00033 LIST_ENTRY(struct character) character_cache;
00034 int dirty_fl;
00035 int refcount;
00036
00037 unsigned id;
00038 struct description_string name, desc;
00039 char *owner, *controllers;
00040 unsigned room_current;
00041 unsigned room_home;
00042 struct attr_list extra_values;
00043 };
00044
00045 LIST_HEAD(struct character_cache, struct character);
00046
00047
00048
00049 extern const struct plugin_character_class plugin_class;
00050
00051
00052
00053
00057 static const struct {
00058 char *name;
00059 enum value_type type;
00060 size_t ofs;
00061 } attrinfo[] = {
00062 {"id", VALUE_TYPE_UINT, offsetof(struct character, id), },
00063 {"name.short", VALUE_TYPE_STRING, offsetof(struct character, name.short_str), },
00064 {"name.long", VALUE_TYPE_STRING, offsetof(struct character, name.long_str), },
00065 {"desc.short", VALUE_TYPE_STRING, offsetof(struct character, desc.short_str), },
00066 {"desc.long", VALUE_TYPE_STRING, offsetof(struct character, desc.long_str), },
00067 {"owner", VALUE_TYPE_STRING, offsetof(struct character, owner), },
00068 {"controllers", VALUE_TYPE_STRING, offsetof(struct character, controllers), },
00069 {"room.current", VALUE_TYPE_UINT, offsetof(struct character, room_current), },
00070 {"room.home", VALUE_TYPE_UINT, offsetof(struct character, room_home), },
00071 };
00072
00074 static struct character_cache character_cache;
00075
00076 static struct freelist character_id_freelist;
00077
00078
00079
00080
00084 static void character_ll_free(struct character *ch) {
00085 unsigned i;
00086
00087 assert(ch != NULL);
00088 if(!ch) return;
00089
00090 LIST_REMOVE(ch, character_cache);
00091 LIST_ENTRY_INIT(ch, character_cache);
00092
00093 for(i=0;i<NR(attrinfo);i++) {
00094 if(attrinfo[i].type==VALUE_TYPE_STRING) {
00095 char **strp=(char**)((char*)ch+attrinfo[i].ofs);
00096 free(*strp);
00097 *strp=NULL;
00098 }
00099 }
00100
00101 attr_list_free(&ch->extra_values);
00102
00103 free(ch);
00104 }
00105
00109 static struct character *character_ll_alloc(void) {
00110 struct character *ret;
00111 ret=calloc(1, sizeof *ret);
00112 if(!ret) {
00113 b_log(B_LOG_CRIT, "character", "out of memory");
00114 }
00115 return ret;
00116 }
00117
00121 static int character_attr_set(struct character *ch, const char *name, const char *value) {
00122 unsigned i;
00123 int res;
00124
00125 assert(ch != NULL);
00126 if(!ch) return 0;
00127
00128 for(i=0;i<NR(attrinfo);i++) {
00129 if(!strcasecmp(name, attrinfo[i].name)) {
00130 ch->dirty_fl=1;
00131 return value_set(value, attrinfo[i].type, (char*)ch+attrinfo[i].ofs);
00132 }
00133 }
00134 res=parse_attr(name, value, &ch->extra_values);
00135 if(res) {
00136 ch->dirty_fl=1;
00137 }
00138 return res;
00139 }
00140
00144 static const char *character_attr_get(struct character *ch, const char *name) {
00145 unsigned i;
00146 struct attr_entry *at;
00147
00148 assert(ch != NULL);
00149 if(!ch) return NULL;
00150
00151 for(i=0;i<NR(attrinfo);i++) {
00152 if(!strcasecmp(name, attrinfo[i].name)) {
00153 return value_get(attrinfo[i].type, (char*)ch+attrinfo[i].ofs);
00154 }
00155 }
00156 at=attr_find(&ch->extra_values, name);
00157 return at ? at->value : NULL;
00158 }
00159
00163 static struct character *character_load(unsigned character_id) {
00164 struct character *ch;
00165 struct fdb_read_handle *h;
00166 const char *name, *value;
00167
00168 assert(character_id > 0);
00169 if(character_id<=0) return NULL;
00170
00171 h=fdb.read_begin_uint(DOMAIN_CHARACTER, character_id);
00172 if(!h) {
00173 b_log(B_LOG_ERROR, "character", "could not load character \"%u\"", character_id);
00174 return NULL;
00175 }
00176
00177 ch=character_ll_alloc();
00178 if(!ch) {
00179 fdb.read_end(h);
00180 return NULL;
00181 }
00182
00183 while(fdb.read_next(h, &name, &value)) {
00184 if(!character_attr_set(ch, name, value)) {
00185 b_log(B_LOG_ERROR, "character", "could not load character \"%u\"", character_id);
00186 character_ll_free(ch);
00187 fdb.read_end(h);
00188 return NULL;
00189 }
00190 }
00191
00192 fdb.read_end(h);
00193
00194 if(character_id!=ch->id) {
00195 b_log(B_LOG_ERROR, "character", "could not load character \"%u\" (bad, missing or mismatched id)", character_id);
00196 character_ll_free(ch);
00197 return NULL;
00198 }
00199 return ch;
00200 }
00201
00205 static int character_save(struct character *ch) {
00206 struct attr_entry *curr;
00207 struct fdb_write_handle *h;
00208 unsigned i;
00209
00210 assert(ch != NULL);
00211 if(!ch->dirty_fl) return 1;
00212
00213 h=fdb.write_begin_uint(DOMAIN_CHARACTER, ch->id);
00214 if(!h) {
00215 b_log(B_LOG_ERROR, "character", "could not save character \"%u\"", ch->id);
00216 return 0;
00217 }
00218
00219 for(i=0;i<NR(attrinfo);i++) {
00220 void *base=((char*)ch+attrinfo[i].ofs);
00221 switch(attrinfo[i].type) {
00222 case VALUE_TYPE_UINT:
00223 fdb.write_format(h, attrinfo[i].name, "%u", *(unsigned*)base);
00224 break;
00225 case VALUE_TYPE_STRING:
00226 if(*(char**)base)
00227 fdb.write_pair(h, attrinfo[i].name, *(char**)base);
00228 break;
00229 }
00230 }
00231 for(curr=LIST_TOP(ch->extra_values);curr;curr=LIST_NEXT(curr, list)) {
00232 fdb.write_pair(h, curr->name, curr->value);
00233 }
00234
00235 if(!fdb.write_end(h)) {
00236 b_log(B_LOG_ERROR, "character", "could not save character \"%u\"", ch->id);
00237 return 0;
00238 }
00239
00240 ch->dirty_fl=0;
00241 b_log(B_LOG_INFO, "character", "saved character \"%u\"", ch->id);
00242 return 1;
00243 }
00244
00249 static struct character *character_get(unsigned character_id) {
00250 struct character *curr;
00251
00252
00253 for(curr=LIST_TOP(character_cache);curr;curr=LIST_NEXT(curr, character_cache)) {
00254 if(curr->id==character_id) break;
00255 }
00256
00257 if(!curr) {
00258
00259 curr=character_load(character_id);
00260 }
00261 if(curr) {
00262
00263 LIST_INSERT_HEAD(&character_cache, curr, character_cache);
00264 curr->refcount++;
00265 }
00266 if(!curr) {
00267 b_log(B_LOG_WARN, "character", "could not access character \"%u\"", character_id);
00268 }
00269 return curr;
00270 }
00271
00275 static void character_put(struct character *ch) {
00276 assert(ch != NULL);
00277
00278 ch->refcount--;
00279
00280 if(ch->refcount<=0) {
00281 character_save(ch);
00282 character_ll_free(ch);
00283 }
00284 }
00285
00286 static struct character *character_new(void) {
00287 struct character *ret;
00288 long id;
00289
00290 ret=character_ll_alloc();
00291 if(!ret) return NULL;
00292
00293
00294 id=freelist_alloc(&character_id_freelist, 1);
00295 if(id<0) {
00296 b_log(B_LOG_CRIT, "character", "could not allocate new character id.");
00297 character_ll_free(ret);
00298 return NULL;
00299 }
00300 ret->id=id;
00301
00302
00303 ret->dirty_fl=1;
00304 character_save(ret);
00305
00306
00307 LIST_INSERT_HEAD(&character_cache, ret, character_cache);
00308 ret->refcount++;
00309 return ret;
00310 }
00311
00315 static int character_preflight(void) {
00316 struct fdb_iterator *it;
00317 const char *id;
00318
00319 it=fdb.iterator_begin(DOMAIN_CHARACTER);
00320 if(!it) {
00321 b_log(B_LOG_CRIT, "character", "could not load characters!");
00322 return 0;
00323 }
00324
00325 while((id=fdb.iterator_next(it))) {
00326 struct character *ch;
00327 unsigned character_id;
00328 char *endptr;
00329 b_log(B_LOG_DEBUG, "character", "Found character: \"%s\"", id);
00330 character_id=strtoul(id, &endptr, 10);
00331 if(*endptr) {
00332 b_log(B_LOG_CRIT, "character", "character id \"%s\" is invalid!", id);
00333 fdb.iterator_end(it);
00334 return 0;
00335 }
00336 ch=character_load(character_id);
00337 if(!ch) {
00338 b_log(B_LOG_CRIT, "character", "could not load character id \"%u\"", character_id);
00339 fdb.iterator_end(it);
00340 return 0;
00341 }
00342
00343 if(ch->id!=character_id) {
00344 b_log(B_LOG_CRIT, "character", "bad or non-matching character id \"%u\"", character_id);
00345 character_ll_free(ch);
00346 fdb.iterator_end(it);
00347 }
00348
00349 if(!freelist_thwack(&character_id_freelist, ch->id, 1)) {
00350 b_log(B_LOG_CRIT, "character", "bad or duplicate character id \"%u\"", character_id);
00351 character_ll_free(ch);
00352 fdb.iterator_end(it);
00353 return 0;
00354 }
00355 character_ll_free(ch);
00356 }
00357 fdb.iterator_end(it);
00358 return 1;
00359 }
00363 static int initialize(void) {
00364 b_log(B_LOG_INFO, "character", "Character plugin loaded (" __FILE__ " compiled " __TIME__ " " __DATE__ ")");
00365 freelist_init(&character_id_freelist);
00366 freelist_pool(&character_id_freelist, 1, ID_MAX);
00367
00368 fdb.domain_init(DOMAIN_CHARACTER);
00369
00370 if(!character_preflight()) {
00371 b_log(B_LOG_CRIT, "character", "could not load characters!");
00372 return 0;
00373 }
00374 service_attach_character(&plugin_class.base_class, &plugin_class.character_interface);
00375 return 1;
00376 }
00377
00381 static int shutdown(void) {
00382 b_log(B_LOG_INFO, "character", "Character plugin shutting down...");
00383 service_detach_character(&plugin_class.base_class);
00384 b_log(B_LOG_INFO, "character", "Character plugin ended.");
00385 return 1;
00386 }
00387
00388
00389
00390
00391
00392
00396 const struct plugin_character_class plugin_class = {
00397 .base_class = { PLUGIN_API, "character", initialize, shutdown },
00398 .character_interface = {
00399 character_get, character_put, character_new,
00400 character_attr_set, character_attr_get,
00401 character_save
00402 },
00403 };