@ -1,19 +1,26 @@
# include "Core.h"
# include "Console.h"
# include "modules/Buildings.h"
# include "modules/Constructions.h"
# include "modules/EventManager.h"
# include "modules/Job.h"
# include "modules/World.h"
# include "df/building.h"
# include "df/construction.h"
# include "df/global_objects.h"
# include "df/item.h"
# include "df/job.h"
# include "df/job_list_link.h"
# include "df/ui.h"
# include "df/unit.h"
# include "df/unit_syndrome.h"
# include "df/world.h"
//#include <list>
# include <map>
//#include <vector>
# include <unordered_map>
# include <unordered_set>
using namespace std ;
using namespace DFHack ;
using namespace EventManager ;
@ -21,17 +28,24 @@ using namespace EventManager;
/*
* TODO :
* error checking
* consider a typedef instead of a struct for EventHandler
* */
//map<uint32_t, vector<DFHack::EventManager::EventHandler> > tickQueue;
multimap < uint32_t , EventHandler > tickQueue ;
//TODO: consider unordered_map of pairs, or unordered_map of unordered_set, or whatever
multimap < Plugin * , EventHandler > handlers [ EventType : : EVENT_MAX ] ;
multimap < Plugin * , int32_t > pluginFrequencies [ EventType : : EVENT_MAX ] ;
map < int32_t , int32_t > eventFrequency [ EventType : : EVENT_MAX ] ;
uint32_t eventLastTick [ EventType : : EVENT_MAX ] ;
const uint32_t ticksPerYear = 403200 ;
void DFHack : : EventManager : : registerListener ( EventType : : EventType e , EventHandler handler , Plugin * plugin ) {
void DFHack : : EventManager : : registerListener ( EventType : : EventType e , EventHandler handler , int32_t freq , Plugin * plugin ) {
handlers [ e ] . insert ( pair < Plugin * , EventHandler > ( plugin , handler ) ) ;
eventFrequency [ e ] [ freq ] + + ;
pluginFrequencies [ e ] . insert ( pair < Plugin * , int32_t > ( plugin , freq ) ) ;
}
void DFHack : : EventManager : : registerTick ( EventHandler handler , int32_t when , Plugin * plugin , bool absolute ) {
@ -52,7 +66,7 @@ void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plug
return ;
}
void DFHack : : EventManager : : unregister ( EventType : : EventType e , EventHandler handler , Plugin * plugin ) {
void DFHack : : EventManager : : unregister ( EventType : : EventType e , EventHandler handler , int32_t freq , Plugin * plugin ) {
for ( multimap < Plugin * , EventHandler > : : iterator i = handlers [ e ] . find ( plugin ) ; i ! = handlers [ e ] . end ( ) ; i + + ) {
if ( ( * i ) . first ! = plugin )
break ;
@ -62,6 +76,16 @@ void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handl
break ;
}
}
if ( eventFrequency [ e ] . find ( freq ) = = eventFrequency [ e ] . end ( ) ) {
Core : : getInstance ( ) . getConsole ( ) . print ( " %s, line %d: Error: incorrect frequency on deregister. \n " , __FILE__ , __LINE__ ) ;
return ;
}
eventFrequency [ e ] [ freq ] - - ;
if ( eventFrequency [ e ] [ freq ] = = 0 ) {
eventFrequency [ e ] . erase ( eventFrequency [ e ] . find ( freq ) ) ;
} else if ( eventFrequency [ e ] [ freq ] < 0 ) {
Core : : getInstance ( ) . getConsole ( ) . print ( " %s, line %d: Error: incorrect frequency on deregister. \n " , __FILE__ , __LINE__ ) ;
}
return ;
}
@ -88,6 +112,21 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) {
for ( size_t a = 0 ; a < ( size_t ) EventType : : EVENT_MAX ; a + + ) {
handlers [ a ] . erase ( plugin ) ;
}
for ( size_t a = 0 ; a < ( size_t ) EventType : : EVENT_MAX ; a + + ) {
for ( auto b = pluginFrequencies [ a ] . begin ( ) ; b ! = pluginFrequencies [ a ] . end ( ) ; b + + ) {
if ( ( * b ) . first ! = plugin )
continue ;
int32_t freq = ( * b ) . second ;
eventFrequency [ a ] [ freq ] - - ;
if ( eventFrequency [ a ] [ freq ] < 0 ) {
Core : : getInstance ( ) . getConsole ( ) . print ( " %s, line %d: Error: incorrect frequency on deregister. \n " , __FILE__ , __LINE__ ) ;
eventFrequency [ a ] . erase ( eventFrequency [ a ] . find ( freq ) ) ;
} else if ( eventFrequency [ a ] [ freq ] = = 0 ) {
eventFrequency [ a ] . erase ( eventFrequency [ a ] . find ( freq ) ) ;
}
}
}
return ;
}
@ -96,14 +135,46 @@ static void manageJobInitiatedEvent(color_ostream& out);
static void manageJobCompletedEvent ( color_ostream & out ) ;
static void manageUnitDeathEvent ( color_ostream & out ) ;
static void manageItemCreationEvent ( color_ostream & out ) ;
static void manageBuildingEvent ( color_ostream & out ) ;
static void manageConstructionEvent ( color_ostream & out ) ;
static void manageSyndromeEvent ( color_ostream & out ) ;
static void manageInvasionEvent ( color_ostream & out ) ;
//tick event
static uint32_t lastTick = 0 ;
//job initiated
static int32_t lastJobId = - 1 ;
static map < int32_t , df : : job * > prevJobs ;
static set < int32_t > livingUnits ;
//job completed
static unordered_map < int32_t , df : : job * > prevJobs ;
//unit death
static unordered_set < int32_t > livingUnits ;
//item creation
static int32_t nextItem ;
//building
static int32_t nextBuilding ;
static unordered_set < int32_t > buildings ;
//construction
static unordered_set < df : : construction * > constructions ;
static bool gameLoaded ;
//invasion
static int32_t nextInvasion ;
void DFHack : : EventManager : : onStateChange ( color_ostream & out , state_change_event event ) {
static bool doOnce = false ;
if ( ! doOnce ) {
//TODO: put this somewhere else
doOnce = true ;
EventHandler buildingHandler ( Buildings : : updateBuildings ) ;
DFHack : : EventManager : : registerListener ( EventType : : BUILDING , buildingHandler , 100 , NULL ) ;
//out.print("Registered listeners.\n %d", __LINE__);
}
if ( event = = DFHack : : SC_MAP_UNLOADED ) {
lastTick = 0 ;
lastJobId = - 1 ;
@ -114,6 +185,13 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
tickQueue . clear ( ) ;
livingUnits . clear ( ) ;
nextItem = - 1 ;
nextBuilding = - 1 ;
buildings . clear ( ) ;
constructions . clear ( ) ;
Buildings : : clearBuildings ( out ) ;
gameLoaded = false ;
nextInvasion = - 1 ;
} else if ( event = = DFHack : : SC_MAP_LOADED ) {
uint32_t tick = DFHack : : World : : ReadCurrentYear ( ) * ticksPerYear
+ DFHack : : World : : ReadCurrentTick ( ) ;
@ -125,25 +203,58 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
tickQueue . insert ( newTickQueue . begin ( ) , newTickQueue . end ( ) ) ;
nextItem = * df : : global : : item_next_id ;
nextItem = 0 ;
nextBuilding = 0 ;
lastTick = 0 ;
nextInvasion = df : : global : : ui - > invasions . next_id ;
gameLoaded = true ;
}
}
void DFHack : : EventManager : : manageEvents ( color_ostream & out ) {
if ( ! Core: : getInstance ( ) . isWorldLoaded ( ) ) {
if ( ! gameLoaded ) {
return ;
}
uint32_t tick = DFHack : : World : : ReadCurrentYear ( ) * ticksPerYear
+ DFHack : : World : : ReadCurrentTick ( ) ;
if ( tick < = lastTick )
return ;
lastTick = tick ;
manageTickEvent ( out ) ;
manageJobInitiatedEvent ( out ) ;
manageJobCompletedEvent ( out ) ;
manageUnitDeathEvent ( out ) ;
manageItemCreationEvent ( out ) ;
if ( tick - eventLastTick [ EventType : : JOB_INITIATED ] > = ( * eventFrequency [ EventType : : JOB_INITIATED ] . begin ( ) ) . first ) {
manageJobInitiatedEvent ( out ) ;
eventLastTick [ EventType : : JOB_INITIATED ] = tick ;
}
if ( tick - eventLastTick [ EventType : : JOB_COMPLETED ] > = ( * eventFrequency [ EventType : : JOB_COMPLETED ] . begin ( ) ) . first ) {
manageJobCompletedEvent ( out ) ;
eventLastTick [ EventType : : JOB_COMPLETED ] = tick ;
}
if ( tick - eventLastTick [ EventType : : UNIT_DEATH ] > = ( * eventFrequency [ EventType : : UNIT_DEATH ] . begin ( ) ) . first ) {
manageUnitDeathEvent ( out ) ;
eventLastTick [ EventType : : UNIT_DEATH ] = tick ;
}
if ( tick - eventLastTick [ EventType : : ITEM_CREATED ] > = ( * eventFrequency [ EventType : : ITEM_CREATED ] . begin ( ) ) . first ) {
manageItemCreationEvent ( out ) ;
eventLastTick [ EventType : : ITEM_CREATED ] = tick ;
}
if ( tick - eventLastTick [ EventType : : BUILDING ] > = ( * eventFrequency [ EventType : : BUILDING ] . begin ( ) ) . first ) {
manageBuildingEvent ( out ) ;
eventLastTick [ EventType : : BUILDING ] = tick ;
}
if ( tick - eventLastTick [ EventType : : CONSTRUCTION ] > = ( * eventFrequency [ EventType : : CONSTRUCTION ] . begin ( ) ) . first ) {
manageConstructionEvent ( out ) ;
eventLastTick [ EventType : : CONSTRUCTION ] = tick ;
}
if ( tick - eventLastTick [ EventType : : SYNDROME ] > = ( * eventFrequency [ EventType : : SYNDROME ] . begin ( ) ) . first ) {
manageSyndromeEvent ( out ) ;
eventLastTick [ EventType : : SYNDROME ] = tick ;
}
if ( tick - eventLastTick [ EventType : : INVASION ] > = ( * eventFrequency [ EventType : : INVASION ] . begin ( ) ) . first ) {
manageInvasionEvent ( out ) ;
eventLastTick [ EventType : : INVASION ] = tick ;
}
return ;
}
@ -201,7 +312,7 @@ static void manageJobCompletedEvent(color_ostream& out) {
nowJobs [ link - > item - > id ] = link - > item ;
}
for ( map < int32_t , df : : job * > : : iterator i = prevJobs . begin ( ) ; i ! = prevJobs . end ( ) ; i + + ) {
for ( auto i = prevJobs . begin ( ) ; i ! = prevJobs . end ( ) ; i + + ) {
if ( nowJobs . find ( ( * i ) . first ) ! = nowJobs . end ( ) )
continue ;
@ -212,13 +323,13 @@ static void manageJobCompletedEvent(color_ostream& out) {
}
//erase old jobs, copy over possibly altered jobs
for ( map < int32_t , df : : job * > : : iterator i = prevJobs . begin ( ) ; i ! = prevJobs . end ( ) ; i + + ) {
for ( auto i = prevJobs . begin ( ) ; i ! = prevJobs . end ( ) ; i + + ) {
Job : : deleteJobStruct ( ( * i ) . second ) ;
}
prevJobs . clear ( ) ;
//create new jobs
for ( map < int32_t , df : : job * > : : iterator j = nowJobs . begin ( ) ; j ! = nowJobs . end ( ) ; j + + ) {
for ( auto j = nowJobs . begin ( ) ; j ! = nowJobs . end ( ) ; j + + ) {
/*map<int32_t, df::job*>::iterator i = prevJobs.find((*j).first);
if ( i ! = prevJobs . end ( ) ) {
continue ;
@ -289,3 +400,122 @@ static void manageItemCreationEvent(color_ostream& out) {
nextItem = * df : : global : : item_next_id ;
}
static void manageBuildingEvent ( color_ostream & out ) {
/*
* TODO : could be faster
* consider looking at jobs : building creation / destruction
* */
if ( handlers [ EventType : : BUILDING ] . empty ( ) )
return ;
multimap < Plugin * , EventHandler > copy ( handlers [ EventType : : BUILDING ] . begin ( ) , handlers [ EventType : : BUILDING ] . end ( ) ) ;
//first alert people about new buildings
for ( int32_t a = nextBuilding ; a < * df : : global : : building_next_id ; a + + ) {
int32_t index = df : : building : : binsearch_index ( df : : global : : world - > buildings . all , a ) ;
if ( index = = - 1 ) {
//out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a);
//the tricky thing is that when the game first starts, it's ok to skip buildings, but otherwise, if you skip buildings, something is probably wrong. TODO: make this smarter
continue ;
}
buildings . insert ( a ) ;
for ( auto b = copy . begin ( ) ; b ! = copy . end ( ) ; b + + ) {
EventHandler bob = ( * b ) . second ;
bob . eventHandler ( out , ( void * ) a ) ;
}
}
nextBuilding = * df : : global : : building_next_id ;
//now alert people about destroyed buildings
unordered_set < int32_t > toDelete ;
for ( auto a = buildings . begin ( ) ; a ! = buildings . end ( ) ; a + + ) {
int32_t id = * a ;
int32_t index = df : : building : : binsearch_index ( df : : global : : world - > buildings . all , id ) ;
if ( index ! = - 1 )
continue ;
toDelete . insert ( id ) ;
for ( auto b = copy . begin ( ) ; b ! = copy . end ( ) ; b + + ) {
EventHandler bob = ( * b ) . second ;
bob . eventHandler ( out , ( void * ) id ) ;
}
}
for ( auto a = toDelete . begin ( ) ; a ! = toDelete . end ( ) ; a + + ) {
int32_t id = * a ;
buildings . erase ( id ) ;
}
//out.print("Sent building event.\n %d", __LINE__);
}
static void manageConstructionEvent ( color_ostream & out ) {
if ( handlers [ EventType : : CONSTRUCTION ] . empty ( ) )
return ;
unordered_set < df : : construction * > constructionsNow ( df : : global : : world - > constructions . begin ( ) , df : : global : : world - > constructions . end ( ) ) ;
multimap < Plugin * , EventHandler > copy ( handlers [ EventType : : CONSTRUCTION ] . begin ( ) , handlers [ EventType : : CONSTRUCTION ] . end ( ) ) ;
for ( auto a = constructions . begin ( ) ; a ! = constructions . end ( ) ; a + + ) {
df : : construction * construction = * a ;
if ( constructionsNow . find ( construction ) ! = constructionsNow . end ( ) )
continue ;
for ( auto b = copy . begin ( ) ; b ! = copy . end ( ) ; b + + ) {
EventHandler handle = ( * b ) . second ;
handle . eventHandler ( out , ( void * ) construction ) ;
}
}
for ( auto a = constructionsNow . begin ( ) ; a ! = constructionsNow . end ( ) ; a + + ) {
df : : construction * construction = * a ;
if ( constructions . find ( construction ) ! = constructions . end ( ) )
continue ;
for ( auto b = copy . begin ( ) ; b ! = copy . end ( ) ; b + + ) {
EventHandler handle = ( * b ) . second ;
handle . eventHandler ( out , ( void * ) construction ) ;
}
}
constructions . clear ( ) ;
constructions . insert ( constructionsNow . begin ( ) , constructionsNow . end ( ) ) ;
}
static void manageSyndromeEvent ( color_ostream & out ) {
if ( handlers [ EventType : : SYNDROME ] . empty ( ) )
return ;
multimap < Plugin * , EventHandler > copy ( handlers [ EventType : : SYNDROME ] . begin ( ) , handlers [ EventType : : SYNDROME ] . end ( ) ) ;
for ( auto a = df : : global : : world - > units . active . begin ( ) ; a ! = df : : global : : world - > units . active . end ( ) ; a + + ) {
df : : unit * unit = * a ;
if ( unit - > flags1 . bits . dead )
continue ;
for ( size_t b = 0 ; b < unit - > syndromes . active . size ( ) ; b + + ) {
df : : unit_syndrome * syndrome = unit - > syndromes . active [ b ] ;
uint32_t startTime = syndrome - > year * ticksPerYear + syndrome - > year_time ;
if ( startTime < = eventLastTick [ EventType : : SYNDROME ] )
continue ;
SyndromeData data ( unit - > id , b ) ;
for ( auto c = copy . begin ( ) ; c ! = copy . end ( ) ; c + + ) {
EventHandler handle = ( * c ) . second ;
handle . eventHandler ( out , ( void * ) & data ) ;
}
}
}
}
static void manageInvasionEvent ( color_ostream & out ) {
if ( handlers [ EventType : : INVASION ] . empty ( ) )
return ;
multimap < Plugin * , EventHandler > copy ( handlers [ EventType : : INVASION ] . begin ( ) , handlers [ EventType : : INVASION ] . end ( ) ) ;
if ( df : : global : : ui - > invasions . next_id < = nextInvasion )
return ;
nextInvasion = df : : global : : ui - > invasions . next_id ;
for ( auto a = copy . begin ( ) ; a ! = copy . end ( ) ; a + + ) {
EventHandler handle = ( * a ) . second ;
handle . eventHandler ( out , ( void * ) nextInvasion ) ;
}
}