/* Crimson2 Mud Server * All source written/copyright Ryan Haksi 1995 * * This source code is proprietary. Use in whole or in part without * explicity permission by the author is strictly prohibited * * Current email address(es): cryogen@infoserve.net * Phone number: (604) 591-5295 * * C4 Script Language written/copyright Cam Lesiuk 1995 * Email: clesiuk@engr.uvic.ca */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #ifndef WIN32 #include <unistd.h> /* for unlink */ #endif #include "crimson2.h" #include "macro.h" #include "log.h" #include "mem.h" #include "str.h" #include "ini.h" #include "extra.h" #include "property.h" #include "code.h" #include "file.h" #include "thing.h" #include "exit.h" #include "index.h" #include "world.h" #include "area.h" WTYPE wTypeList[] = { { "INDOORS", 1 }, /* easy as it gets */ { "CITY-OUTSIDE", 2 }, /* pavement, surfaced whatever */ { "FIELD", 3 }, /* uneven ground */ { "FOREST", 4 }, /* breaking through undergrowth */ { "HILLS", 4 }, /* not as bad as mountains */ { "MOUNTAIN", 5 }, /* ah, mountain climbing */ { "WATER-SWIM", 4 }, /* hard work no? */ { "WATER-NOSWIM", 1 }, /* assumed paddling boat */ { "UNDERWATER", 4 }, /* tough swimming */ { "VACUUM", 3 }, /* walk with care */ { "DESERT", 3 }, /* sloggin through the sand */ { "ARCTIC", 3 }, /* snow/ice */ { "ROAD", 1 }, /* easy as it gets */ { "TRAIL", 2 }, /* easy as it gets */ { "", 0 } }; BYTE *wFlagList[] = { "DARK", /* cant see in here without light */ "DEATHTRAP", /* instakill mortal visators */ "NOMOB", /* mobs wont enter this room */ "NOWEATHER", /* noweather */ "NOGOOD", /* good auras cant enter */ "NONEUTRAL", /* neutral auras cant enter */ "NOEVIL", /* evil auras cant enter */ "NOPSIONIC", /* psionics wont work here */ "SMALL", /* only two people at a time */ "PRIVATE", /* no eavesdropping in here */ "DRAINPOWER", /* slowly drain away power points */ "VACUUM", /* explosive decompression in here */ "NOTELEPORTOUT", /* block teleports in */ "NOTELEPORTIN", /* block teleports out */ "" }; BYTE worldOfLog; /* log if a virtual # cant be found */ BYTE worldReadLog; /* log each world structure as its read */ LWORD worldNum = 0; /* number of world structures in use - just curious really */ void WorldInit(void) { BYTE buf[256]; worldOfLog = INILWordRead("crimson2.ini", "worldOfLog", 0); worldReadLog = INILWordRead("crimson2.ini", "worldReadLog", 0); sprintf(buf, "Reading World logging defaults\n"); Log(LOG_BOOT, buf); sprintf(buf, "World structure size is %d bytes\n", sizeof(WLD)); Log(LOG_BOOT, buf); } void WorldRead(WORD area) { FILE *worldFile; BYTE wld[256]; BYTE buf[256]; BYTE tmp[256]; LWORD last = -1; STR *sKey; STR *sDesc; FLAG eFlag; WORD eKeyObj; LWORD eWorld; BYTE eDir; WLD *world; sprintf(wld, "area/%s.wld", areaList[area].aFileName->sText); worldFile = fopen(wld, "rb"); if (!worldFile) { sprintf(buf, "Unable to read %s, killing server\n", wld); Log(LOG_BOOT, buf); PERROR("WorldRead"); exit(ERROR_BADFILE); } areaList[area].aOffset = 0; /* okay we opened it up so read it.... */ while (!feof(worldFile)) { fscanf(worldFile, " %s \n", tmp); /* get virtual number */ if (tmp[0] == '$' || feof(worldFile)) break; /* Dikumud file format EOF character */ if (tmp[0] != '#') { /* whoa... whadda we got here */ sprintf(buf, " Unknown keyword %s, aborting\n", tmp); Log(LOG_BOOT, buf); break; } MEMALLOC(world, WLD, WORLD_ALLOC_SIZE); memset( (void*)world, 0, sizeof(WLD)); /* init to zeros */ world->wVirtual = atol(tmp+1); /* Relocation Code - do we relocate? */ if (!areaList[area].aWldIndex.iNum && world->wVirtual!=areaList[area].aVirtualMin) { sprintf(buf, "Relocating Area/World [%s]\n", areaList[area].aFileName->sText); Log(LOG_BOOT, buf); areaList[area].aOffset = areaList[area].aVirtualMin - world->wVirtual; BITSET(areaList[area].aSystem, AS_WLDUNSAVED|AS_MOBUNSAVED|AS_OBJUNSAVED|AS_RSTUNSAVED); } world->wVirtual += areaList[area].aOffset; if (worldReadLog) { sprintf(buf, "Reading World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } /* confirm that virtual number is valid - warn for out of order */ if (world->wVirtual < last) { sprintf(buf, "%s - room%s < than previous\n", wld, tmp); Log(LOG_BOOT, buf); } last = MAXV(last, world->wVirtual); if (world->wVirtual < areaList[area].aVirtualMin) { sprintf(buf, "%s - room%s < than %ld\n", wld, tmp, areaList[area].aVirtualMin); Log(LOG_BOOT, buf); break; } if (world->wVirtual > areaList[area].aVirtualMax) { sprintf(buf, "%s - room%s > than %ld\n", wld, tmp, areaList[area].aVirtualMax); Log(LOG_BOOT, buf); break; } /* NOTE that Private Strings are not designated as such, until just prior to editing */ worldNum++; world->wArea = area; Thing(world)->tType = TTYPE_WLD; Thing(world)->tSDesc = FileStrRead(worldFile); if (fileError) { sprintf(buf, "Error reading World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } Thing(world)->tDesc = FileStrRead(worldFile); if (fileError) { sprintf(buf, "Error reading World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } fscanf(worldFile, " %*d "); /* in original diku this was the zone #, but isnt used anymore */ world->wFlag = FileFlagRead(worldFile, wFlagList); /* flags for this room */ world->wType = FILETYPEREAD(worldFile, wTypeList); /* type of this room, ie city, river, underwater etc */ if (fileError) { sprintf(buf, "Error reading wType for World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } /* read attachments ie exits, keywords etc */ while (!feof(worldFile)) { fscanf(worldFile, " %s \n", tmp); if (tmp[0] == 'S') /* well thats it for this chunk of the world */ break; else if (tmp[0] == 'D') { /* haha an exit */ eDir = atoi(tmp+1); sDesc = FileStrRead(worldFile); if (fileError) { sprintf(buf, "Error reading ExitDesc for World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } /* Should have a trailing return */ if (sDesc->sText[sDesc->sLen-1] != '\n' && sDesc->sLen>0) { BYTE hugeBuf[4096]; sprintf(hugeBuf, "%s\n", sDesc->sText); STRFREE(sDesc); sDesc = STRCREATE(hugeBuf); } sKey = FileStrRead(worldFile); if (fileError) { sprintf(buf, "Error reading ExitKey for World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } eFlag = FileFlagRead(worldFile, eFlagList); /* flags for this exit ie closed locked etc */ fscanf(worldFile, " %hd ", &eKeyObj); /* room this goes to */ fscanf(worldFile, " %ld ", &eWorld); /* room this goes to */ eWorld = AreaMove(area, eWorld); world->wExit = /* will find the pointer to the room l8r */ ExitAlloc(world->wExit, eDir, sKey, sDesc, eFlag, eKeyObj, Thing(eWorld)); } else if (tmp[0] == 'E') { /* haha an extra */ sKey = FileStrRead(worldFile); if (fileError) { sprintf(buf, "Error reading ExtraKey for World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } sDesc = FileStrRead(worldFile); if (fileError) { sprintf(buf, "Error reading ExtraDesc for World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } Thing(world)->tExtra = ExtraAlloc(Thing(world)->tExtra, sKey, sDesc); } else if (tmp[0] == 'P') { /* a property of some kind */ sKey = FileStrRead(worldFile); /* property name */ if (fileError) { sprintf(buf, "Error reading PropertyKey for World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } sDesc = FileStrRead(worldFile);/* property value */ if (fileError) { sprintf(buf, "Error reading PropertyDesc for World#%ld\n", world->wVirtual); Log(LOG_BOOT, buf); } Thing(world)->tProperty = PropertyCreate(Thing(world)->tProperty, sKey, sDesc); } } /* guess this ones a keeper, update mins and maxes etc... */ IndexInsert(&areaList[area].aWldIndex, Thing(world), WorldCompareProc); if (indexError) { sprintf(buf, "%s - duplicate insert for room%s\n", wld, tmp); Log(LOG_BOOT, buf); THINGFREE( Thing(world) ); } } /* all done close up shop */ fclose(worldFile); } THING *WorldOf(LWORD virtual) { THING *search; BYTE buf[256]; LWORD area; area = AreaOf(virtual); if (area == -1) return NULL; search = IndexFind(&areaList[area].aWldIndex, (void*)virtual, WorldFindProc); if (search) { return search; } else { if (worldOfLog) { sprintf(buf, "WorldOf: virtual %ld doesnt exist\n", virtual); Log(LOG_ERROR, buf); } } return NULL; } /* Once we have finished reading everything in, re-index all entries exits */ void WorldReIndex(void) { LWORD area; LWORD world; EXIT *exit; Log(LOG_BOOT, "Re-Indexing World\n"); for (area=0; area<areaListMax; area++) { for (world=0; world<areaList[area].aWldIndex.iNum; world++) { /* re-index all the exits in this room */ for (exit=Wld(areaList[area].aWldIndex.iThing[world])->wExit; exit; exit=exit->eNext){ exit->eWorld = WorldOf((LWORD) exit->eWorld); } } } } void WorldWrite(WORD area) { FILE *worldFile; BYTE wld[128]; BYTE buf[256]; THING *world; EXTRA *extra; LWORD wVirtual; PROPERTY *property; EXIT *exit; LWORD i; /* take a backup case we crash halfways through this */ sprintf(buf, "mv area/wld/%s.wld area/wld/%s.wld.bak", areaList[area].aFileName->sText, areaList[area].aFileName->sText); system(buf); sprintf(wld, "area/%s.wld", areaList[area].aFileName->sText); if (areaList[area].aWldIndex.iNum == 0) { Log(LOG_AREA, wld); LogPrintf(LOG_AREA, ": nothing to save.\n"); return; } worldFile = fopen(wld, "wb"); if (!worldFile) { sprintf(buf, "Unable to write %s!\n", wld); Log(LOG_ERROR, buf); PERROR("WorldWrite"); } /* okay we opened it up so write it.... */ for (i=0; i<areaList[area].aWldIndex.iNum; i++) { world = Thing(areaList[area].aWldIndex.iThing[i]); fprintf(worldFile, "#%ld\n", Wld(world)->wVirtual); /* get virtual number */ FileStrWrite(worldFile, world->tSDesc); FileStrWrite(worldFile, world->tDesc); fprintf(worldFile, "%hd ", area); FileFlagWrite(worldFile, Wld(world)->wFlag, wFlagList, ' '); FILETYPEWRITE(worldFile, Wld(world)->wType, wTypeList, '\n'); /* read attachments ie exits, keywords etc */ for (exit=Wld(world)->wExit; exit; exit=exit->eNext) { fprintf(worldFile, "D%d\n", (int)exit->eDir); FileStrWrite(worldFile, exit->eDesc); FileStrWrite(worldFile, exit->eKey); if (exit->eWorld) { wVirtual=Wld(exit->eWorld)->wVirtual; } else { wVirtual=-1; } FileFlagWrite(worldFile, exit->eFlag, eFlagList, ' '); fprintf(worldFile, "%ld %ld\n", exit->eKeyObj, wVirtual); } /* extra descriptions */ for (extra=world->tExtra; extra; extra=extra->eNext) { fprintf(worldFile, "E\n"); FileStrWrite(worldFile, extra->eKey); FileStrWrite(worldFile, extra->eDesc); } /* property's */ for (property=world->tProperty; property; property=property->pNext) { fprintf(worldFile, "P\n"); FileStrWrite(worldFile, property->pKey); if ( CodeIsCompiled(property) ) { if (!areaWriteBinary) { /* Decompile the code b4 saving */ CodeDecompProperty(property, NULL); /* Warn if it didnt decompile */ if (CodeIsCompiled(property)) { sprintf(buf, "WorldWrite: Property %s failed to decompile for world#%ld!\n", property->pKey->sText, Wld(world)->wVirtual); Log(LOG_AREA, buf); } else { CodeClearFlag(world, property); FileStrWrite(worldFile, property->pDesc); /* if there is a player in the room - recompile the code */ if (world->tContain) { if (!CodeCompileProperty(property, NULL)) CodeSetFlag(world, property); BITCLR(world->tFlag, TF_COMPILE); } else { BITSET(world->tFlag, TF_COMPILE); } } } else { FileBinaryWrite(worldFile, property->pDesc); } } else { FileStrWrite(worldFile, property->pDesc); } } fprintf(worldFile, "S\n"); } /* all done close up shop */ fclose(worldFile); /* turf backup we didnt crash */ sprintf(buf, "area/%s.wld.bak", areaList[area].aFileName->sText); unlink(buf); } INDEXPROC(WorldCompareProc) { /* BYTE IndexProc(void *index1, void *index2) */ if ( Wld(index1)->wVirtual == Wld(index2)->wVirtual ) return 0; else if ( Wld(index1)->wVirtual < Wld(index2)->wVirtual ) return -1; else return 1; } INDEXFINDPROC(WorldFindProc) { /* BYTE IFindProc(void *key, void *index) */ if ( (LWORD)key == Wld(index)->wVirtual ) return 0; else if ( (LWORD)key < Wld(index)->wVirtual ) return -1; else return 1; }