[Add] First release of unidiff - Save changes to the universe in a diff format.

This commit is contained in:
Allanis 2013-12-10 22:51:00 +00:00
parent fef6cefe05
commit fdd5e930f5
8 changed files with 702 additions and 25 deletions

View File

@ -1,6 +1,6 @@
# OPTIONS. # OPTIONS.
DEBUG := 1 #DEBUG := 1
#DEBUG_PARANOID := 1 DEBUG_PARANOID := 1
OS := LINUX OS := LINUX
#OS := WIN32 #OS := WIN32

11
dat/unidiff.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<unidiffs>
<unidiff name="collective_dead">
<system name="C-28">
<planet name="S38729">remove</planet>
<fleet name="Collective Sml Swarm" chance="60">remove</fleet>
<fleet name="Collective Sml Swarm" chance="60">remove</fleet>
<fleet name="Collective Drone" chance="80">remove</fleet>
</system>
</unidiff>
</unidiffs>

View File

@ -23,6 +23,7 @@
#include "ltime.h" #include "ltime.h"
#include "xml.h" #include "xml.h"
#include "music.h" #include "music.h"
#include "unidiff.h"
#include "misn_lua.h" #include "misn_lua.h"
/* Similar to lua vars, but with less variety. */ /* Similar to lua vars, but with less variety. */
@ -156,6 +157,17 @@ static const luaL_reg hook_methods[] = {
{ 0, 0 } { 0, 0 }
}; };
/* Diffs. */
static int diff_applyL(lua_State* L);
static int diff_removeL(lua_State* L);
static int diff_isappliedL(lua_State* L);
static const luaL_reg diff_methods[] = {
{ "apply", diff_applyL },
{ "remove", diff_removeL },
{ "isApplied", diff_isappliedL },
{ 0, 0 }
};
/* Register all the libaries. */ /* Register all the libaries. */
int misn_loadLibs(lua_State* L) { int misn_loadLibs(lua_State* L) {
lua_loadLephisto(L); lua_loadLephisto(L);
@ -915,3 +927,74 @@ static int hook_pilot(lua_State* L) {
return 0; return 0;
} }
/**
* @defgroup DIFF Universe Diff Lua Bindings.
*
* @brief Lua bindings to apply/remove Universe Diffs.
*
* Functions should be called like:
*
* @code
* diff.function(parameters)
* @endcode
*
* @{
*/
/**
* @fn static int diff_applyL(lua_State* L)
*
* @brief apply(string name)
*
* Applies a diff by name.
* @param name Name of the diff to apply.
*/
static int diff_applyL(lua_State* L) {
char* name;
if(lua_isstring(L, 1)) name = (char*)lua_tostring(L, 1);
else LLUA_INVALID_PARAMETER();
diff_apply(name);
return 0;
}
/**
* @fn static int diff_removeL(lua_State* L)
*
* @brief remove(string name)
*
* Removes a diff by name.
* @param name Name of the diff to remove.
*/
static int diff_removeL(lua_State* L) {
char* name;
if(lua_isstring(L, 1)) name = (char*)lua_tostring(L, 1);
else LLUA_INVALID_PARAMETER();
diff_remove(name);
return 0;
}
/**
* @fn static int diff_isappliedL(lua_State* L)
*
* @brief bool isApplied(string name)
*
* Check to see if a diff is currently applied.
* @param name Name of the diff to check.
* @return true if it is applied, false if it isn't.
*/
static int diff_isappliedL(lua_State* L) {
char* name;
if(lua_isstring(L, 1)) name = (char*)lua_tostring(L, 1);
else LLUA_INVALID_PARAMETER();
lua_pushboolean(L, diff_isApplied(name));
return 1;
}
/**
* @}
*/

View File

@ -32,6 +32,8 @@ extern int hook_save(xmlTextWriterPtr writer); /* Hooks. */
extern int hook_load(xmlNodePtr parent); extern int hook_load(xmlNodePtr parent);
extern int space_sysSave(xmlTextWriterPtr writer); /* Space stuff. */ extern int space_sysSave(xmlTextWriterPtr writer); /* Space stuff. */
extern int space_sysLoad(xmlNodePtr parent); extern int space_sysLoad(xmlNodePtr parent);
extern int diff_save(xmlTextWriterPtr writer);
extern int diff_load(xmlNodePtr parent);
extern void menu_main_close(void); extern void menu_main_close(void);
/* Static. */ /* Static. */
static int save_data(xmlTextWriterPtr writer); static int save_data(xmlTextWriterPtr writer);
@ -48,6 +50,7 @@ static int save_data(xmlTextWriterPtr writer) {
if(var_save(writer) < 0) return -1; if(var_save(writer) < 0) return -1;
if(pfaction_save(writer) < 0) return -1; if(pfaction_save(writer) < 0) return -1;
if(hook_save(writer) < 0) return -1; if(hook_save(writer) < 0) return -1;
if(diff_save(writer) < 0) return -1;
if(space_sysSave(writer) < 0) return -1; if(space_sysSave(writer) < 0) return -1;
return 0; return 0;
} }
@ -212,6 +215,7 @@ static int load_game(char* file) {
missions_loadActive(node); missions_loadActive(node);
pfaction_load(node); pfaction_load(node);
hook_load(node); hook_load(node);
diff_load(node);
space_sysLoad(node); space_sysLoad(node);
/* Need to run takeoff hooks since player just "took off". */ /* Need to run takeoff hooks since player just "took off". */

View File

@ -727,6 +727,89 @@ static int planet_parse(Planet* planet, const xmlNodePtr parent) {
return 0; return 0;
} }
/**
* @fn int system_addPlanet(StarSystem* sys, char* planetname)
*
* @brief Add a planet to a star system.
* @param sys Star System to add planet to.
* @param planetname Name of the planet to add.
* @return 0 on success.
*/
int system_addPlanet(StarSystem* sys, char* planetname) {
Planet* planet;
if(sys == NULL)
return -1;
/* Check if need to grow the star system planet stack. */
sys->nplanets++;
if(sys->planets == NULL) {
sys->planets = malloc(sizeof(Planet*) * CHUNK_SIZE_SMALL);
}
else if(sys->nplanets > CHUNK_SIZE_SMALL) {
sys->planets = realloc(sys->planets, sizeof(Planet*) * sys->nplanets);
}
planet = planet_get(planetname);
sys->planets[sys->nplanets-1] = planet;
/* Add planet <-> star system to name stack. */
spacename_nstack++;
if(spacename_nstack > spacename_mstack) {
spacename_stack += CHUNK_SIZE;
planetname_stack = realloc(planetname_stack,
sizeof(char*) * spacename_mstack);
systemname_stack = realloc(systemname_stack,
sizeof(char*) * spacename_mstack);
}
planetname_stack[spacename_nstack-1] = planet->name;
systemname_stack[spacename_nstack-1] = sys->name;
return 0;
}
/**
* @fn int system_rmPlanet(StarSystem* sys, char* planetname)
*
* @brief Remove a planet from a star system.
* @param sys Star System to remove planet from.
* @param planetname Name of the planet to remove.
* @return 0 on success.
*/
int system_rmPlanet(StarSystem* sys, char* planetname) {
int i;
Planet* planet;
if(sys == NULL)
return -1;
/* Try to find planet. */
planet = planet_get(planetname);
for(i = 0; i < sys->nplanets; i++)
if(sys->planets[i] == planet)
break;
/* Planet not found. */
if(i >= sys->nplanets)
return -1;
/* Remove planet from system. */
sys->nplanets--;
memmove(&sys->planets[i], &sys->planets[i+1], sizeof(Planet*) * (sys->nplanets-i));
/* Remove from the name stack thingy. */
for(i = 0; i < spacename_nstack; i++)
if(strcmp(planetname, planetname_stack[i])==0) {
spacename_nstack--;
memmove(&planetname_stack[i], &planetname_stack[i+1],
sizeof(char*) * (spacename_nstack-i));
memmove(&systemname_stack[i], &systemname_stack[i+1],
sizeof(char*) * (spacename_nstack-i));
break;
}
return 0;
}
/* Parse node 'parent' which should be the node of a system. */ /* Parse node 'parent' which should be the node of a system. */
/* Return the StarSystem fully loaded. */ /* Return the StarSystem fully loaded. */
static StarSystem* system_parse(StarSystem* sys, const xmlNodePtr parent) { static StarSystem* system_parse(StarSystem* sys, const xmlNodePtr parent) {
@ -790,29 +873,8 @@ static StarSystem* system_parse(StarSystem* sys, const xmlNodePtr parent) {
else if(xml_isNode(node, "planets")) { else if(xml_isNode(node, "planets")) {
cur = node->children; cur = node->children;
do { do {
if(cur && xml_isNode(cur, "planet")) { if(xml_isNode(cur, "planet"))
/* Add planet to system. */ system_addPlanet(sys, xml_get(cur));
sys->nplanets++;
if(sys->nplanets > size) {
size += CHUNK_SIZE_SMALL;
sys->planets = realloc(sys->planets, sizeof(Planet*) * size);
}
planet = planet_get(xml_get(cur));
sys->planets[sys->nplanets-1] = planet;
/* Add planet <-> star system to name stack. */
spacename_nstack++;
if(spacename_nstack > spacename_mstack) {
spacename_mstack += CHUNK_SIZE;
planetname_stack = realloc(planetname_stack,
sizeof(char*) * spacename_mstack);
systemname_stack = realloc(systemname_stack,
sizeof(char*) * spacename_mstack);
}
planetname_stack[spacename_nstack-1] = planet->name;
systemname_stack[spacename_nstack-1] = sys->name;
}
} while(xml_nextNode(cur)); } while(xml_nextNode(cur));
} }
/* Load all the fleets. */ /* Load all the fleets. */

View File

@ -142,6 +142,8 @@ void space_exit(void);
char* planet_getSystem(char* planetname); char* planet_getSystem(char* planetname);
Planet* planet_get(char* planetname); Planet* planet_get(char* planetname);
char planet_getClass(Planet* p); char planet_getClass(Planet* p);
int system_addPlanet(StarSystem* sys, char* planetname);
int system_rmPlanet(StarSystem* sys, char* planetname);
/* Render. */ /* Render. */
void space_render(const double dt); void space_render(const double dt);

508
src/unidiff.c Normal file
View File

@ -0,0 +1,508 @@
/**
* @file unidiff.c
*
* @brief Handles the application and removal of 'diffs' to the universe.
*
* Diffs allow changing planets, fleets, factions etc.. in the universe.
* These are meant to be applied after the player triggers them, mostly
* through missions.
*/
#include <malloc.h>
#include <string.h>
#include "lephisto.h"
#include "log.h"
#include "xml.h"
#include "space.h"
#include "pack.h"
#include "unidiff.h"
#define CHUNK_SIZE 32 /**< Size of chunk to allocate. */
#define DIFF_DATA "../dat/unidiff.xml" /**< Unidff XML file. */
/**
* @enum UniHunkTargetType_t
*
* @brief Represents the possible hunk targets.
*/
typedef enum UniHunkTargetType_ {
HUNK_TARGET_NONE,
HUNK_TARGET_SYSTEM
} UniHunkTargetType_t;
/**
* @struct UniHunkTarget_t
*
* @brief Represents the hunks target.
*/
typedef struct UniHunkTarget_ {
UniHunkTargetType_t type;
union {
char* name;
} u;
} UniHunkTarget_t;
/**
* @enum UniHunkType_t
*
* @brief Represents the different type of hunk actions.
*/
typedef enum UniHunkType_ {
HUNK_TYPE_NONE,
/* Target should be system. */
HUNK_TYPE_PLANET_ADD,
HUNK_TYPE_PLANET_REMOVE,
HUNK_TYPE_FLEET_ADD,
HUNK_TYPE_FLEET_REMOVE
} UniHunkType_t;
/**
* @struct UniHunk_t
*
* @brief Represents a single hunk in the diff.
*/
typedef struct UniHunk_ {
UniHunkTarget_t target; /**< Hunk's target. */
UniHunkType_t type; /**< Type of hunk it is. */
union {
char* name;
} u; /* Actual data to patch. */
} UniHunk_t;
/**
* @struct UniDiff_t
*
* @brief Represents each Universe Diff.
*/
typedef struct UniDiff_ {
char* name; /**< Name of the diff. */
UniHunk_t* applied; /**< Applied hunks. */
int napplied; /**< Number of applied hunks. */
int mapplied; /**< Memory of applied hunks. */
UniHunk_t* failed; /**< Failed hunks. */
int nfailed; /**< Number of failed hunks. */
int mfailed; /**< Memory of failed hunks. */
} UniDiff_t;
/* Diff stack. */
static UniDiff_t* diff_stack = NULL; /**< Currently applied universe diffs. */
static int diff_nstack = 0; /**< Number of diffs in the stack. */
static int diff_mstack = 0; /**< Currently allocated diffs. */
static UniDiff_t* diff_get(char* name);
static UniDiff_t* diff_newDiff(void);
static int diff_removeDiff(UniDiff_t* diff);
static int diff_patch(xmlNodePtr parent);
static int diff_patchHunk(UniHunk_t* hunk);
static void diff_hunkFailed(UniDiff_t* diff, UniHunk_t* hunk);
static void diff_hunkSuccess(UniDiff_t* diff, UniHunk_t* hunk);
static void diff_cleanup(UniDiff_t* diff);
static void diff_cleanupHunk(UniHunk_t* hunk);
/* Externed. */
int diff_save(xmlTextWriterPtr writer);
int diff_load(xmlNodePtr parent);
/**
* @fn int diff_isApplied(char* name)
*
* @brief Check if a diff is currently applied.
* @param name Diff to check.
* @return 0 if it's not applied, 1 if it is.
*/
int diff_isApplied(char* name) {
if(diff_get(name) != NULL)
return 1;
return 0;
}
/**
* @fn static UniDiff_t* diff_get(char* name)
*
* @brief Get a diff by name.
* @param name Name of the diff to get.
* @return The diff if found or NULL if not found.
*/
static UniDiff_t* diff_get(char* name) {
int i;
for(i = 0; i < diff_nstack; i++)
if(strcmp(diff_stack[i].name, name)==0)
return &diff_stack[i];
return NULL;
}
/**
* @fn int diff_apply(char* name)
*
* @brief Applies a diff to the universe.
* @param name Diff to apply.
* @return 0 on success.
*/
int diff_apply(char* name) {
xmlNodePtr node;
xmlDocPtr doc;
uint32_t bufsize;
char* buf;
char* diffname;
/* Check if already applied. */
if(diff_isApplied(name))
return 0;
buf = pack_readfile(DATA, DIFF_DATA, &bufsize);
node = doc->xmlChildrenNode;
if(strcmp((char*)node->name, "unidiffs")) {
WARN("Malformed unidiff file: missing root element 'unidiffs'");
return 0;
}
node = node->xmlChildrenNode; /* First system node. */
if(node == NULL) {
ERR("Malformed unidiff file: does not contain elements");
return 0;
}
do {
if(xml_isNode(node, "unidiff")) {
/* Check to see if it's the diff we're looking for. */
xmlr_attr(node, "name", diffname);
if(strcmp(diffname, name)==0) {
diff_patch(node);
free(diffname);
return 0;
}
free(diffname);
}
} while(xml_nextNode(node));
WARN("UniDiff '%s' not found in "DIFF_DATA".", name);
return -1;
}
/**
* @fn static int diff_patch(xmlNodePtr parent)
*
* @brief Actually applies a diff in XML node form.
* @param parent Node containing the diff information.
* @return 0 on success.
*/
static int diff_patch(xmlNodePtr parent) {
UniDiff_t* diff;
UniHunk_t base, hunk;
xmlNodePtr node, cur;
char* buf;
/* Prepare it. */
diff = diff_newDiff();
memset(diff, 0, sizeof(UniDiff_t));
xmlr_attr(parent, "name", diff->name);
node = parent->xmlChildrenNode;
do {
if(xml_isNode(node, "system")) {
/* Set the target. */
memset(&base, 0, sizeof(UniHunk_t));
base.target.type = HUNK_TARGET_SYSTEM;
xmlr_attr(node, "name", base.target.u.name);
if(base.target.u.name == NULL)
WARN("UniDiff '%s' has a system node without a 'name' tag", diff->name);
/* Now parse the possible changes. */
cur = node->xmlChildrenNode;
do {
if(xml_isNode(cur, "planet")) {
memcpy(&hunk, &base, sizeof(UniHunk_t));
/* Get the planet to modify. */
xmlr_attr(cur, "name", hunk.u.name);
/* Get the type. */
buf = xml_get(cur);
if(strcmp(buf, "add")==0) {
hunk.type = HUNK_TYPE_PLANET_ADD;
}
else if(strcmp(buf, "remove")==0) {
hunk.type = HUNK_TYPE_PLANET_REMOVE;
}
if(diff_patchHunk(&hunk) < 0)
diff_hunkFailed(diff, &hunk);
else
diff_hunkSuccess(diff, &hunk);
}
else if(xml_isNode(cur, "fleet")) {
memcpy(&hunk, &base, sizeof(UniHunk_t));
/* Get the planet to modify. */
xmlr_attr(cur, "name", hunk.u.name);
/* Get the type. */
buf = xml_get(cur);
if(strcmp(buf, "add")==0) {
hunk.type = HUNK_TYPE_FLEET_ADD;
}
else if(strcmp(buf, "remove")==0) {
hunk.type = HUNK_TYPE_FLEET_REMOVE;
}
if(diff_patchHunk(&hunk) < 0)
diff_hunkFailed(diff, &hunk);
else
diff_hunkSuccess(diff, &hunk);
}
} while(xml_nextNode(cur));
}
} while(xml_nextNode(node));
return 0;
}
/**
* @fn static int diff_patchHunk(UniHunk_t* hunk)
*
* @brief Applies a hunk and adds it to the diff.
* @param diff Diff to which the hunk belongs.
* @param hunk Hunk to apply.
*/
static int diff_patchHunk(UniHunk_t* hunk) {
switch(hunk->type) {
/* Adding a planet. */
case HUNK_TYPE_PLANET_ADD:
return system_addPlanet(system_get(hunk->target.u.name), hunk->u.name);
/* Removing a planet. */
case HUNK_TYPE_PLANET_REMOVE:
return system_rmPlanet(system_get(hunk->target.u.name), hunk->u.name);
case HUNK_TYPE_FLEET_ADD:
break;
case HUNK_TYPE_FLEET_REMOVE:
break;
default:
WARN("Unkown hunk type '%d'.", hunk->type);
break;
}
return -1;
}
/**
* @fn static void diff_hunkFailed(UniDiff_t* diff, UniHunk_t* hunk)
*
* @brief Add a hunk to the failed list.
* @param diff Diff to add hunk to.
* @param hunk Hunk that failed to apply.
*/
static void diff_hunkFailed(UniDiff_t* diff, UniHunk_t* hunk) {
if(diff == NULL)
return;
diff->nfailed++;
if(diff->nfailed > diff->mfailed) {
diff->mfailed += CHUNK_SIZE;
diff->failed = realloc(diff->failed, sizeof(UniHunk_t) * diff->mfailed);
}
memcpy(&diff->failed[diff->nfailed-1], &hunk, sizeof(UniHunk_t));
}
/**
* @fn static void diff_hunkSuccess(UniDiff_t* diff, UniHunk_t* hunk)
*
* @brief Add a hunk to the applied list.
* @param diff Diff to add hunk to.
* @param hunk Hunk that applied correctly.
*/
static void diff_hunkSuccess(UniDiff_t* diff, UniHunk_t* hunk) {
if(diff == NULL)
return;
diff->napplied++;
if(diff->napplied > diff->mapplied) {
diff->mapplied += CHUNK_SIZE;
diff->applied = realloc(diff->applied, sizeof(UniHunk_t) * diff->mapplied);
}
memcpy(&diff->applied[diff->napplied-1], &hunk, sizeof(UniHunk_t));
}
/**
* @fn void diff_remove(char* name)
*
* @brief Remove a diff from the universe.
* @param name Diff to remove.
*/
void diff_remove(char* name) {
UniDiff_t* diff;
/* check if already applied. */
diff = diff_get(name);
if(diff == NULL)
return;
diff_removeDiff(diff);
}
/**
* @fn void diff_clear(void)
*/
void diff_clear(void) {
while(diff_nstack > 0) {
diff_removeDiff(&diff_stack[diff_nstack-1]);
}
}
/**
* @fn static UniDiff_t* diff_newDiff(void)
*
* @brief Creates a new UniDiff_t for usage.
* @return A newly created UniDiff_t.
*/
static UniDiff_t* diff_newDiff(void) {
/* Check if needs initialization. */
if(diff_stack == NULL) {
diff_mstack = CHUNK_SIZE;
diff_stack = malloc(diff_mstack * sizeof(UniDiff_t));
diff_nstack = 0;
return &diff_stack[0];
}
diff_nstack++;
/* Check if need to grow. */
if(diff_nstack > diff_mstack) {
diff_mstack += CHUNK_SIZE;
diff_stack = realloc(diff_stack, diff_mstack * sizeof(UniDiff_t));
}
return &diff_stack[diff_nstack-1];
}
/**
* @fn static int diff_removeDiff(UniDiff_t* diff)
*
* @brief Removes a diff.
* @param diff Diff to remove.
* @return 0 on success.
*/
static int diff_removeDiff(UniDiff_t* diff) {
int i;
UniHunk_t hunk;
for(i = 0; i < diff->napplied; i++) {
memcpy(&hunk, &diff->applied[i], sizeof(UniHunk_t));
/* Invert the type for reverting. */
switch(hunk.type) {
case HUNK_TYPE_PLANET_ADD:
hunk.type = HUNK_TYPE_PLANET_REMOVE;
break;
case HUNK_TYPE_PLANET_REMOVE:
hunk.type = HUNK_TYPE_PLANET_ADD;
break;
case HUNK_TYPE_FLEET_ADD:
hunk.type = HUNK_TYPE_FLEET_REMOVE;
break;
case HUNK_TYPE_FLEET_REMOVE:
hunk.type = HUNK_TYPE_FLEET_ADD;
break;
default:
WARN("Unkown Hunk type '%d'.", hunk.type);
continue;
}
diff_patchHunk(&hunk);
}
diff_cleanup(diff);
diff_nstack--;
i = diff - diff_stack;
memmove(&diff_stack[i], &diff_stack[i+1], sizeof(UniDiff_t) * (diff_nstack-i));
return 0;
}
/**
* @fn static void diff_cleanup(UniDiff_t* diff)
*
* @brief Clean up a diff.
* @param diff Diff to clean up.
*/
static void diff_cleanup(UniDiff_t* diff) {
int i;
free(diff->name);
for(i = 0; i < diff->napplied; i++)
diff_cleanupHunk(&diff->applied[i]);
if(diff->applied != NULL)
free(diff->applied);
for(i = 0; i < diff->nfailed; i++)
diff_cleanupHunk(&diff->failed[i]);
if(diff->failed != NULL)
free(diff->failed);
memset(diff, 0, sizeof(UniDiff_t));
}
/**
* @fn static void diff_cleanupHunk(UniHunk_t* hunk)
*
* @brief Clean up a hunk.
* @param hunk Hunk to clean up.
*/
static void diff_cleanupHunk(UniHunk_t* hunk) {
if(hunk->target.u.name != NULL)
free(hunk->target.u.name);
if(hunk->u.name != NULL)
free(hunk->u.name);
memset(hunk, 0, sizeof(UniHunk_t));
}
/**
* @fn int diff_save(xmlTextWriterPtr writer)
*
* @brief Save the active diffs.
* @param writer XML Writer to use.
* @return 0 on success.
*/
int diff_save(xmlTextWriterPtr writer) {
int i;
UniDiff_t* diff;
xmlw_startElem(writer, "diffs");
for(i = 0; i < diff_nstack; i++) {
diff = &diff_stack[i];
xmlw_elem(writer, "diff", diff->name);
}
xmlw_endElem(writer); /* "diffs" */
return 0;
}
/**
* @fn int diff_load(xmlNodePtr parent)
*
* @brief Loads the diffs.
* @param parent Parent node containing diffs.
* @return 0 on success.
*/
int diff_load(xmlNodePtr parent) {
xmlNodePtr node, cur;
diff_clear();
node = parent->xmlChildrenNode;
do {
if(xml_isNode(node, "diffs")) {
cur = node->xmlChildrenNode;
do {
if(xml_isNode(cur, "diff"))
diff_apply(xml_get(cur));
} while(xml_nextNode(cur));
}
} while(xml_nextNode(node));
return 0;
}

7
src/unidiff.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
int diff_apply(char* name);
void diff_remove(char* name);
void diff_clear(void);
int diff_isApplied(char* name);