1
0
forked from 0ad/0ad

# Enabled some interoperability between different compiler versions for the game and the editor DLL

This was SVN commit r3790.
This commit is contained in:
Ykkrosh 2006-04-21 03:53:10 +00:00
parent 8bb97e63cd
commit d9b033c85f
31 changed files with 569 additions and 161 deletions

View File

@ -14,6 +14,26 @@
#include "wx/config.h" #include "wx/config.h"
#include "wx/debugrpt.h" #include "wx/debugrpt.h"
// Shared memory allocation functions
ATLASDLLIMPEXP void* ShareableMalloc(size_t n)
{
// TODO: make sure this is thread-safe everywhere. (It is in MSVC with the
// multithreaded CRT.)
return malloc(n);
}
ATLASDLLIMPEXP void ShareableFree(void* p)
{
return free(p);
}
// Define the function pointers that we'll use when calling those functions.
// (The game loads the addresses of the above functions, then does the same.)
namespace AtlasMessage
{
void* (*ShareableMallocFptr) (size_t) = &ShareableMalloc;
void (*ShareableFreeFptr) (void*) = &ShareableFree;
}
// Global variables, to remember state between DllMain and StartWindow and OnInit // Global variables, to remember state between DllMain and StartWindow and OnInit
wxString g_InitialWindowType; wxString g_InitialWindowType;
HINSTANCE g_Module; HINSTANCE g_Module;
@ -22,20 +42,20 @@ bool g_IsLoaded = false;
BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID WXUNUSED(lpReserved)) BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID WXUNUSED(lpReserved))
{ {
switch (fdwReason) switch (fdwReason)
{ {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:
g_Module = hModule; g_Module = hModule;
return TRUE; return TRUE;
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:
if (g_IsLoaded) if (g_IsLoaded)
{ {
wxEntryCleanup(); wxEntryCleanup();
g_IsLoaded = false; g_IsLoaded = false;
} }
break; break;
} }
return TRUE; return TRUE;
} }
@ -166,4 +186,4 @@ public:
} }
}; };
IMPLEMENT_APP_NO_MAIN(wxDLLApp) IMPLEMENT_APP_NO_MAIN(wxDLLApp)

View File

@ -1,8 +1,8 @@
#include <wchar.h> #include <wchar.h>
namespace AtlasMessage { class MessageHandler; } namespace AtlasMessage { class MessagePasser; }
ATLASDLLIMPEXP void Atlas_SetMessageHandler(AtlasMessage::MessageHandler*); ATLASDLLIMPEXP void Atlas_SetMessagePasser(AtlasMessage::MessagePasser*);
ATLASDLLIMPEXP void Atlas_StartWindow(wchar_t* type); ATLASDLLIMPEXP void Atlas_StartWindow(wchar_t* type);
ATLASDLLIMPEXP void Atlas_GLSetCurrent(void* context); ATLASDLLIMPEXP void Atlas_GLSetCurrent(void* context);

View File

@ -39,7 +39,7 @@ public:
// Be careful not to send 'resize' messages to the game before we've // Be careful not to send 'resize' messages to the game before we've
// told it that this canvas exists // told it that this canvas exists
if (! m_SuppressResize) if (! m_SuppressResize)
POST_MESSAGE(ResizeScreen(GetClientSize().GetWidth(), GetClientSize().GetHeight())); POST_MESSAGE(ResizeScreen, (GetClientSize().GetWidth(), GetClientSize().GetHeight()));
// TODO: fix flashing // TODO: fix flashing
#endif // UI_ONLY #endif // UI_ONLY
} }
@ -74,14 +74,14 @@ public:
if (dir == -1) // changed modifier keys - update all currently-scrolling directions if (dir == -1) // changed modifier keys - update all currently-scrolling directions
{ {
if (wxGetKeyState(WXK_LEFT)) POST_MESSAGE(ScrollConstant(AtlasMessage::eScrollConstantDir::LEFT, speed)); if (wxGetKeyState(WXK_LEFT)) POST_MESSAGE(ScrollConstant, (AtlasMessage::eScrollConstantDir::LEFT, speed));
if (wxGetKeyState(WXK_RIGHT)) POST_MESSAGE(ScrollConstant(AtlasMessage::eScrollConstantDir::RIGHT, speed)); if (wxGetKeyState(WXK_RIGHT)) POST_MESSAGE(ScrollConstant, (AtlasMessage::eScrollConstantDir::RIGHT, speed));
if (wxGetKeyState(WXK_UP)) POST_MESSAGE(ScrollConstant(AtlasMessage::eScrollConstantDir::FORWARDS, speed)); if (wxGetKeyState(WXK_UP)) POST_MESSAGE(ScrollConstant, (AtlasMessage::eScrollConstantDir::FORWARDS, speed));
if (wxGetKeyState(WXK_DOWN)) POST_MESSAGE(ScrollConstant(AtlasMessage::eScrollConstantDir::BACKWARDS, speed)); if (wxGetKeyState(WXK_DOWN)) POST_MESSAGE(ScrollConstant, (AtlasMessage::eScrollConstantDir::BACKWARDS, speed));
} }
else else
{ {
POST_MESSAGE(ScrollConstant(dir, enable ? speed : 0.0f)); POST_MESSAGE(ScrollConstant, (dir, enable ? speed : 0.0f));
} }
#endif // UI_ONLY #endif // UI_ONLY
return true; return true;
@ -130,7 +130,7 @@ public:
float speed = 16.f; float speed = 16.f;
if (wxGetKeyState(WXK_SHIFT)) if (wxGetKeyState(WXK_SHIFT))
speed *= 4.f; speed *= 4.f;
POST_MESSAGE(SmoothZoom(speed*dir)); POST_MESSAGE(SmoothZoom, (speed*dir));
} }
else else
evt.Skip(); evt.Skip();
@ -210,7 +210,7 @@ public:
else if (wxGetKeyState(WXK_SHIFT)) else if (wxGetKeyState(WXK_SHIFT))
speed *= 4.f; speed *= 4.f;
POST_MESSAGE(SmoothZoom(evt.GetWheelRotation() * speed / evt.GetWheelDelta())); POST_MESSAGE(SmoothZoom, (evt.GetWheelRotation() * speed / evt.GetWheelDelta()));
} }
else else
{ {
@ -229,8 +229,8 @@ public:
switch (m_MouseState) switch (m_MouseState)
{ {
case NONE: break; case NONE: break;
case SCROLL: POST_MESSAGE(Scroll(AtlasMessage::eScrollType::FROM, evt.GetPosition())); break; case SCROLL: POST_MESSAGE(Scroll, (AtlasMessage::eScrollType::FROM, evt.GetPosition())); break;
case ROTATEAROUND: POST_MESSAGE(RotateAround(AtlasMessage::eRotateAroundType::FROM, evt.GetPosition())); break; case ROTATEAROUND: POST_MESSAGE(RotateAround, (AtlasMessage::eRotateAroundType::FROM, evt.GetPosition())); break;
default: wxFAIL; default: wxFAIL;
} }
m_LastMouseState = m_MouseState; m_LastMouseState = m_MouseState;
@ -240,8 +240,8 @@ public:
switch (m_MouseState) switch (m_MouseState)
{ {
case NONE: break; case NONE: break;
case SCROLL: POST_MESSAGE(Scroll(AtlasMessage::eScrollType::TO, evt.GetPosition())); break; case SCROLL: POST_MESSAGE(Scroll, (AtlasMessage::eScrollType::TO, evt.GetPosition())); break;
case ROTATEAROUND: POST_MESSAGE(RotateAround(AtlasMessage::eRotateAroundType::TO, evt.GetPosition())); break; case ROTATEAROUND: POST_MESSAGE(RotateAround, (AtlasMessage::eRotateAroundType::TO, evt.GetPosition())); break;
default: wxFAIL; default: wxFAIL;
} }
} }
@ -432,17 +432,17 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent)
// Send setup messages to game engine: // Send setup messages to game engine:
#ifndef UI_ONLY #ifndef UI_ONLY
POST_MESSAGE(SetContext(canvas->GetContext())); POST_MESSAGE(SetContext, (canvas->GetContext()));
POST_MESSAGE(CommandString("init")); POST_MESSAGE(CommandString, ("init"));
canvas->InitSize(); canvas->InitSize();
// Start with a blank map (so that the editor can assume there's always // Start with a blank map (so that the editor can assume there's always
// a valid map loaded) // a valid map loaded)
POST_MESSAGE(GenerateMap(9)); POST_MESSAGE(GenerateMap, (9));
POST_MESSAGE(CommandString("render_enable")); POST_MESSAGE(CommandString, ("render_enable"));
#endif #endif
// Set up a timer to make sure tool-updates happen frequently (in addition // Set up a timer to make sure tool-updates happen frequently (in addition
@ -458,7 +458,7 @@ void ScenarioEditor::OnClose(wxCloseEvent&)
SetCurrentTool(_T("")); SetCurrentTool(_T(""));
#ifndef UI_ONLY #ifndef UI_ONLY
POST_MESSAGE(CommandString("shutdown")); POST_MESSAGE(CommandString, ("shutdown"));
#endif #endif
AtlasMessage::qExit().Post(); AtlasMessage::qExit().Post();
@ -510,17 +510,17 @@ void ScenarioEditor::OnRedo(wxCommandEvent&)
void ScenarioEditor::OnWireframe(wxCommandEvent& event) void ScenarioEditor::OnWireframe(wxCommandEvent& event)
{ {
POST_MESSAGE(RenderStyle(event.IsChecked())); POST_MESSAGE(RenderStyle, (event.IsChecked()));
} }
void ScenarioEditor::OnMessageTrace(wxCommandEvent& event) void ScenarioEditor::OnMessageTrace(wxCommandEvent& event)
{ {
POST_MESSAGE(MessageTrace(event.IsChecked())); POST_MESSAGE(MessageTrace, (event.IsChecked()));
} }
void ScenarioEditor::OnScreenshot(wxCommandEvent& WXUNUSED(event)) void ScenarioEditor::OnScreenshot(wxCommandEvent& WXUNUSED(event))
{ {
POST_MESSAGE(Screenshot(10)); POST_MESSAGE(Screenshot, (10));
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -25,7 +25,7 @@ static void LoadMap(void*)
SetCurrentTool(_T("")); SetCurrentTool(_T(""));
// TODO: clear the undo buffer, etc // TODO: clear the undo buffer, etc
POST_MESSAGE(LoadMap(map)); POST_MESSAGE(LoadMap, (map));
} }
wxCHECK_RET(cwd == wxFileName::GetCwd(), _T("cwd changed")); wxCHECK_RET(cwd == wxFileName::GetCwd(), _T("cwd changed"));
@ -44,13 +44,13 @@ static void SaveMap(void*)
{ {
// TODO: Work when the map is not in .../maps/scenarios/ // TODO: Work when the map is not in .../maps/scenarios/
std::wstring map = dlg.GetFilename().c_str(); std::wstring map = dlg.GetFilename().c_str();
POST_MESSAGE(SaveMap(map)); POST_MESSAGE(SaveMap, (map));
} }
} }
static void GenerateMap(void*) static void GenerateMap(void*)
{ {
POST_MESSAGE(GenerateMap(9)); POST_MESSAGE(GenerateMap, (9));
} }
static void GenerateRMS(void* data) static void GenerateRMS(void* data)
@ -64,7 +64,7 @@ static void GenerateRMS(void* data)
wxExecute(argv, wxEXEC_SYNC); wxExecute(argv, wxEXEC_SYNC);
wxFileName::SetCwd(cwd); wxFileName::SetCwd(cwd);
POST_MESSAGE(LoadMap(L"_atlasrm.pmp")); POST_MESSAGE(LoadMap, (L"_atlasrm.pmp"));
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -91,7 +91,7 @@ void ObjectSidebar::OnFirstDisplay()
{ {
AtlasMessage::qGetObjectsList qry; AtlasMessage::qGetObjectsList qry;
qry.Post(); qry.Post();
p->m_Objects = qry.objects; p->m_Objects = *qry.objects;
SetObjectFilter(0); SetObjectFilter(0);
} }

View File

@ -80,10 +80,16 @@ public:
AtlasMessage::qGetTerrainGroupPreviews qry(m_Name.c_str(), imageWidth, imageHeight); AtlasMessage::qGetTerrainGroupPreviews qry(m_Name.c_str(), imageWidth, imageHeight);
qry.Post(); qry.Post();
std::vector<AtlasMessage::sTerrainGroupPreview> previews = *qry.previews;
int i = 0; int i = 0;
for (std::vector<AtlasMessage::sTerrainGroupPreview>::iterator it = qry.previews.begin(); it != qry.previews.end(); ++it) for (std::vector<AtlasMessage::sTerrainGroupPreview>::iterator it = previews.begin(); it != previews.end(); ++it)
{ {
wxImage img (imageWidth, imageHeight, it->imagedata); unsigned char* buf = (unsigned char*)(malloc(it->imagedata.GetSize()));
// it->imagedata.GetBuffer() gives a Shareable<unsigned char>*, which
// is stored the same as a unsigned char*, so we can just copy it.
memcpy(buf, it->imagedata.GetBuffer(), it->imagedata.GetSize());
wxImage img (imageWidth, imageHeight, buf);
imglist->Add(wxBitmap(img)); imglist->Add(wxBitmap(img));
wxListItem item; wxListItem item;
@ -142,7 +148,8 @@ public:
// Get the list of terrain groups from the engine // Get the list of terrain groups from the engine
AtlasMessage::qGetTerrainGroups qry; AtlasMessage::qGetTerrainGroups qry;
qry.Post(); qry.Post();
for (std::vector<std::wstring>::iterator it = qry.groupnames.begin(); it != qry.groupnames.end(); ++it) std::vector<std::wstring> groupnames = *qry.groupnames;
for (std::vector<std::wstring>::iterator it = groupnames.begin(); it != groupnames.end(); ++it)
m_TerrainGroups.Add(it->c_str()); m_TerrainGroups.Add(it->c_str());
for (size_t i = 0; i < m_TerrainGroups.GetCount(); ++i) for (size_t i = 0; i < m_TerrainGroups.GetCount(); ++i)

View File

@ -27,7 +27,7 @@ public:
void OnDisable() void OnDisable()
{ {
POST_MESSAGE(BrushPreview(false, Position())); POST_MESSAGE(BrushPreview, (false, Position()));
} }
@ -49,7 +49,7 @@ public:
} }
else if (evt.Moving()) else if (evt.Moving())
{ {
POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition()))); POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition())));
return true; return true;
} }
else else
@ -65,7 +65,7 @@ public:
{ {
void OnEnter(AlterElevation* obj) void OnEnter(AlterElevation* obj)
{ {
POST_MESSAGE(BrushPreview(true, obj->m_Pos)); POST_MESSAGE(BrushPreview, (true, obj->m_Pos));
} }
void OnLeave(AlterElevation*) void OnLeave(AlterElevation*)
@ -84,7 +84,7 @@ public:
{ {
wxPoint pos = evt.GetPosition(); wxPoint pos = evt.GetPosition();
obj->m_Pos = Position(pos); obj->m_Pos = Position(pos);
POST_MESSAGE(BrushPreview(true, obj->m_Pos)); POST_MESSAGE(BrushPreview, (true, obj->m_Pos));
return true; return true;
} }
else else

View File

@ -40,12 +40,12 @@ int Brush::GetHeight() const
return GetWidth(); return GetWidth();
} }
float* Brush::GetNewedData() const std::vector<float> Brush::GetData() const
{ {
int width = GetWidth(); int width = GetWidth();
int height = GetHeight(); int height = GetHeight();
float* data = new float[width*height]; std::vector<float> data (width*height);
switch (m_Shape) switch (m_Shape)
{ {
@ -200,7 +200,7 @@ void Brush::MakeActive()
void Brush::Send() void Brush::Send()
{ {
if (m_IsActive) if (m_IsActive)
POST_MESSAGE(Brush(GetWidth(), GetHeight(), GetNewedData())); POST_MESSAGE(Brush, (GetWidth(), GetHeight(), GetData()));
} }

View File

@ -16,7 +16,7 @@ public:
int GetWidth() const; int GetWidth() const;
int GetHeight() const; int GetHeight() const;
float* GetNewedData() const; // freshly allocated via new[] std::vector<float> GetData() const;
float GetStrength() const; float GetStrength() const;

View File

@ -111,16 +111,20 @@ WorldCommand::WorldCommand(AtlasMessage::mWorldCommand* command)
WorldCommand::~WorldCommand() WorldCommand::~WorldCommand()
{ {
// m_Command was allocated by POST_COMMAND
delete m_Command; delete m_Command;
} }
bool WorldCommand::Do() bool WorldCommand::Do()
{ {
if (m_AlreadyDone) if (m_AlreadyDone)
POST_MESSAGE(RedoCommand()); POST_MESSAGE(RedoCommand, ());
else else
{ {
POST_MESSAGE(DoCommand(m_Command)); // The DoCommand message clones the data from m_Command, and posts that
// (passing ownership to the game), so we're free to delete m_Command
// at any time
POST_MESSAGE(DoCommand, (m_Command));
m_AlreadyDone = true; m_AlreadyDone = true;
} }
return true; return true;
@ -128,7 +132,7 @@ bool WorldCommand::Do()
bool WorldCommand::Undo() bool WorldCommand::Undo()
{ {
POST_MESSAGE(UndoCommand()); POST_MESSAGE(UndoCommand, ());
return true; return true;
} }
@ -145,6 +149,6 @@ bool WorldCommand::Merge(AtlasWindowCommand* p)
if (! m_Command->IsMergeable()) if (! m_Command->IsMergeable())
return false; return false;
POST_MESSAGE(MergeCommand()); POST_MESSAGE(MergeCommand, ());
return true; return true;
} }

View File

@ -26,7 +26,7 @@ public:
void OnDisable() void OnDisable()
{ {
POST_MESSAGE(BrushPreview(false, Position())); POST_MESSAGE(BrushPreview, (false, Position()));
} }
@ -42,7 +42,7 @@ public:
} }
else if (evt.Moving()) else if (evt.Moving())
{ {
POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition()))); POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition())));
return true; return true;
} }
else else
@ -58,7 +58,7 @@ public:
{ {
void OnEnter(FlattenElevation* obj) void OnEnter(FlattenElevation* obj)
{ {
POST_MESSAGE(BrushPreview(true, obj->m_Pos)); POST_MESSAGE(BrushPreview, (true, obj->m_Pos));
} }
void OnLeave(FlattenElevation*) void OnLeave(FlattenElevation*)
@ -77,7 +77,7 @@ public:
{ {
wxPoint pos = evt.GetPosition(); wxPoint pos = evt.GetPosition();
obj->m_Pos = Position(pos); obj->m_Pos = Position(pos);
POST_MESSAGE(BrushPreview(true, obj->m_Pos)); POST_MESSAGE(BrushPreview, (true, obj->m_Pos));
return true; return true;
} }
else else

View File

@ -28,7 +28,7 @@ public:
void OnDisable() void OnDisable()
{ {
POST_MESSAGE(BrushPreview(false, Position())); POST_MESSAGE(BrushPreview, (false, Position()));
} }
@ -50,7 +50,7 @@ public:
} }
else if (evt.Moving()) else if (evt.Moving())
{ {
POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition()))); POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition())));
return true; return true;
} }
else else
@ -96,7 +96,7 @@ public:
void Paint(PaintTerrain* obj) void Paint(PaintTerrain* obj)
{ {
POST_MESSAGE(BrushPreview(true, obj->m_Pos)); POST_MESSAGE(BrushPreview, (true, obj->m_Pos));
POST_COMMAND(PaintTerrain, (obj->m_Pos, g_SelectedTexture.c_str(), GetPriority())); POST_COMMAND(PaintTerrain, (obj->m_Pos, g_SelectedTexture.c_str(), GetPriority()));
} }

View File

@ -30,9 +30,9 @@ public:
+ (m_ScreenPos.type1.y-m_Target.type1.y)*(m_ScreenPos.type1.y-m_Target.type1.y); + (m_ScreenPos.type1.y-m_Target.type1.y)*(m_ScreenPos.type1.y-m_Target.type1.y);
bool useTarget = (dragDistSq >= 16*16); bool useTarget = (dragDistSq >= 16*16);
if (preview) if (preview)
POST_MESSAGE(ObjectPreview(m_ObjectID.c_str(), 0, m_ObjPos, useTarget, m_Target, g_DefaultAngle)); POST_MESSAGE(ObjectPreview, (m_ObjectID.c_str(), 0, m_ObjPos, useTarget, m_Target, g_DefaultAngle));
else else
POST_COMMAND(CreateObject,(m_ObjectID.c_str(), m_Player, m_ObjPos, useTarget, m_Target, g_DefaultAngle)); POST_COMMAND(CreateObject, (m_ObjectID.c_str(), m_Player, m_ObjPos, useTarget, m_Target, g_DefaultAngle));
} }
virtual void Init(void* initData) virtual void Init(void* initData)

View File

@ -23,7 +23,7 @@ public:
void OnDisable() void OnDisable()
{ {
m_Selection.clear(); m_Selection.clear();
POST_MESSAGE(SetSelectionPreview(m_Selection)); POST_MESSAGE(SetSelectionPreview, (m_Selection));
} }
@ -46,7 +46,7 @@ public:
obj->m_dy = qry.offsety; obj->m_dy = qry.offsety;
SET_STATE(Dragging); SET_STATE(Dragging);
} }
POST_MESSAGE(SetSelectionPreview(obj->m_Selection)); POST_MESSAGE(SetSelectionPreview, (obj->m_Selection));
ScenarioEditor::GetCommandProc().FinaliseLastCommand(); ScenarioEditor::GetCommandProc().FinaliseLastCommand();
return true; return true;
} }
@ -68,7 +68,7 @@ public:
for (size_t i = 0; i < obj->m_Selection.size(); ++i) for (size_t i = 0; i < obj->m_Selection.size(); ++i)
POST_COMMAND(DeleteObject, (obj->m_Selection[i])); POST_COMMAND(DeleteObject, (obj->m_Selection[i]));
obj->m_Selection.clear(); obj->m_Selection.clear();
POST_MESSAGE(SetSelectionPreview(obj->m_Selection)); POST_MESSAGE(SetSelectionPreview, (obj->m_Selection));
return true; return true;
} }
else else

View File

@ -49,22 +49,20 @@ public:
}; };
Brush::Brush() Brush::Brush()
: m_W(0), m_H(0), m_TerrainOverlay(NULL), m_Data(NULL) : m_W(0), m_H(0), m_TerrainOverlay(NULL)
{ {
} }
Brush::~Brush() Brush::~Brush()
{ {
delete m_TerrainOverlay; delete m_TerrainOverlay;
delete[] m_Data;
} }
void Brush::SetData(int w, int h, const float* data) void Brush::SetData(int w, int h, const std::vector<float>& data)
{ {
m_W = w; m_W = w;
m_H = h; m_H = h;
delete[] m_Data;
m_Data = data; m_Data = data;
} }

View File

@ -9,7 +9,7 @@ struct Brush
Brush(); Brush();
~Brush(); ~Brush();
void SetData(int w, int h, const float* data); void SetData(int w, int h, const std::vector<float>& data);
void SetRenderEnabled(bool enabled); // initial state is disabled void SetRenderEnabled(bool enabled); // initial state is disabled
@ -27,7 +27,7 @@ struct Brush
CVector3D m_Centre; CVector3D m_Centre;
private: private:
TerrainOverlay* m_TerrainOverlay; // NULL if rendering is not enabled TerrainOverlay* m_TerrainOverlay; // NULL if rendering is not enabled
const float* m_Data; std::vector<float> m_Data;
}; };
extern Brush g_CurrentBrush; extern Brush g_CurrentBrush;

View File

@ -2,6 +2,8 @@
#include <list> #include <list>
#include <map> #include <map>
#include "SharedMemory.h"
namespace AtlasMessage namespace AtlasMessage
{ {
@ -47,7 +49,8 @@ struct DataCommand : public Command // so commands can optionally override (De|C
{ {
void Destruct() {}; void Destruct() {};
void Construct() {}; void Construct() {};
// MergeWithSelf should update 'prev' to include the effects of 'this' // MergeWithSelf should be overriden by commands, and implemented
// to update 'prev' to include the effects of 'this'
void MergeWithSelf(void*) { debug_warn("MergeWithSelf unimplemented in some command"); } void MergeWithSelf(void*) { debug_warn("MergeWithSelf unimplemented in some command"); }
}; };
@ -57,8 +60,8 @@ struct DataCommand : public Command // so commands can optionally override (De|C
d##t* d; \ d##t* d; \
public: \ public: \
c##t(d##t* data) : d(data) { Construct(); } \ c##t(d##t* data) : d(data) { Construct(); } \
~c##t() { Destruct(); delete d; } \ ~c##t() { Destruct(); AtlasMessage::ShareableDelete(d); /* d was allocated by mDoCommand() */ } \
static Command* Create(const void* data) { return new c##t((d##t*)data); } \ static Command* Create(const void* data) { return new c##t ((d##t*)data); } \
virtual void Merge(Command* prev) { MergeWithSelf((c##t*)prev); } \ virtual void Merge(Command* prev) { MergeWithSelf((c##t*)prev); } \
virtual const char* GetType() const { return #t; } virtual const char* GetType() const { return #t; }

View File

@ -4,6 +4,7 @@
#include "MessagePasserImpl.h" #include "MessagePasserImpl.h"
#include "Messages.h" #include "Messages.h"
#include "SharedMemory.h"
#include "Brushes.h" #include "Brushes.h"
#include "Handlers/MessageHandler.h" #include "Handlers/MessageHandler.h"
@ -41,6 +42,11 @@ void (*Atlas_SetMessagePasser)(MessagePasser*);
void (*Atlas_GLSetCurrent)(void* context); void (*Atlas_GLSetCurrent)(void* context);
void (*Atlas_GLSwapBuffers)(void* context); void (*Atlas_GLSwapBuffers)(void* context);
void (*Atlas_NotifyEndOfFrame)(); void (*Atlas_NotifyEndOfFrame)();
namespace AtlasMessage
{
void* (*ShareableMallocFptr)(size_t);
void (*ShareableFreeFptr)(void*);
}
static MessagePasserImpl msgPasser; static MessagePasserImpl msgPasser;
@ -71,6 +77,10 @@ bool BeginAtlas(int argc, char* argv[], void* dll)
GET(Atlas_GLSwapBuffers); GET(Atlas_GLSwapBuffers);
GET(Atlas_NotifyEndOfFrame); GET(Atlas_NotifyEndOfFrame);
#undef GET #undef GET
#define GET(x) *(void**)&x##Fptr = dlsym(dll, #x); debug_assert(x##Fptr); if (! x##Fptr) return false;
GET(ShareableMalloc);
GET(ShareableFree);
#undef GET
// Pass our message handler to Atlas // Pass our message handler to Atlas
Atlas_SetMessagePasser(&msgPasser); Atlas_SetMessagePasser(&msgPasser);
@ -129,7 +139,7 @@ bool BeginAtlas(int argc, char* argv[], void* dll)
// construct a reference to the appropriate handler for the // construct a reference to the appropriate handler for the
// given string) // given string)
name += "_"; name += "_";
name += static_cast<mCommandString*>(msg)->name; name += *static_cast<mCommandString*>(msg)->name;
// use 'static_cast' when casting messages, to make it clear // use 'static_cast' when casting messages, to make it clear
// that something slightly dangerous is happening - we have // that something slightly dangerous is happening - we have
// to just assume that GetName is correct, since we can't use // to just assume that GetName is correct, since we can't use
@ -166,7 +176,7 @@ bool BeginAtlas(int argc, char* argv[], void* dll)
{ {
// For non-queries, we need to delete the object, since we // For non-queries, we need to delete the object, since we
// took ownership of it. // took ownership of it.
delete msg; AtlasMessage::ShareableDelete(msg);
} }
} }
} }

View File

@ -8,13 +8,13 @@ namespace AtlasMessage {
MESSAGEHANDLER(Brush) MESSAGEHANDLER(Brush)
{ {
g_CurrentBrush.SetData(msg->width, msg->height, msg->data); g_CurrentBrush.SetData(msg->width, msg->height, *msg->data);
} }
MESSAGEHANDLER(BrushPreview) MESSAGEHANDLER(BrushPreview)
{ {
g_CurrentBrush.SetRenderEnabled(msg->enable); g_CurrentBrush.SetRenderEnabled(msg->enable);
msg->pos.GetWorldSpace(g_CurrentBrush.m_Centre); msg->pos->GetWorldSpace(g_CurrentBrush.m_Centre);
} }
} }

View File

@ -53,14 +53,14 @@ MESSAGEHANDLER(Scroll)
if (msg->type == eScrollType::FROM) if (msg->type == eScrollType::FROM)
{ {
msg->pos.GetWorldSpace(targetPos); msg->pos->GetWorldSpace(targetPos);
targetDistance = (targetPos - camera.GetTranslation()).GetLength(); targetDistance = (targetPos - camera.GetTranslation()).GetLength();
} }
else if (msg->type == eScrollType::TO) else if (msg->type == eScrollType::TO)
{ {
CVector3D origin, dir; CVector3D origin, dir;
float x, y; float x, y;
msg->pos.GetScreenSpace(x, y); msg->pos->GetScreenSpace(x, y);
g_Game->GetView()->GetCamera()->BuildCameraRay((int)x, (int)y, origin, dir); g_Game->GetView()->GetCamera()->BuildCameraRay((int)x, (int)y, origin, dir);
dir *= targetDistance; dir *= targetDistance;
camera.Translate(targetPos - dir - origin); camera.Translate(targetPos - dir - origin);
@ -87,13 +87,13 @@ MESSAGEHANDLER(RotateAround)
if (msg->type == eRotateAroundType::FROM) if (msg->type == eRotateAroundType::FROM)
{ {
msg->pos.GetScreenSpace(lastX, lastY); // get mouse position msg->pos->GetScreenSpace(lastX, lastY); // get mouse position
msg->pos.GetWorldSpace(focusPos); // get point on terrain under mouse msg->pos->GetWorldSpace(focusPos); // get point on terrain under mouse
} }
else if (msg->type == eRotateAroundType::TO) else if (msg->type == eRotateAroundType::TO)
{ {
float x, y; float x, y;
msg->pos.GetScreenSpace(x, y); // get mouse position msg->pos->GetScreenSpace(x, y); // get mouse position
// Rotate around X and Y axes by amounts depending on the mouse delta // Rotate around X and Y axes by amounts depending on the mouse delta
float rotX = 6.f * (y-lastY) / g_Renderer.GetHeight(); float rotX = 6.f * (y-lastY) / g_Renderer.GetHeight();

View File

@ -10,7 +10,7 @@ namespace AtlasMessage {
MESSAGEHANDLER(DoCommand) MESSAGEHANDLER(DoCommand)
{ {
Command* c = NULL; Command* c = NULL;
cmdHandlers::const_iterator it = GetCmdHandlers().find("c" + msg->name); cmdHandlers::const_iterator it = GetCmdHandlers().find("c" + *msg->name);
if (it != GetCmdHandlers().end()) if (it != GetCmdHandlers().end())
{ {
c = (it->second)(msg->data); c = (it->second)(msg->data);

View File

@ -104,7 +104,7 @@ BEGIN_COMMAND(AlterElevation)
} }
static CVector3D previousPosition; static CVector3D previousPosition;
d->pos.GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition); d->pos->GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition);
previousPosition = g_CurrentBrush.m_Centre; previousPosition = g_CurrentBrush.m_Centre;
int x0, y0; int x0, y0;
@ -163,7 +163,7 @@ BEGIN_COMMAND(FlattenElevation)
int amount = (int)d->amount; int amount = (int)d->amount;
static CVector3D previousPosition; static CVector3D previousPosition;
d->pos.GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition); d->pos->GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition);
previousPosition = g_CurrentBrush.m_Centre; previousPosition = g_CurrentBrush.m_Centre;
int xc, yc; int xc, yc;

View File

@ -98,14 +98,14 @@ MESSAGEHANDLER(GenerateMap)
MESSAGEHANDLER(LoadMap) MESSAGEHANDLER(LoadMap)
{ {
InitGame(msg->filename); InitGame(*msg->filename);
StartGame(); StartGame();
} }
MESSAGEHANDLER(SaveMap) MESSAGEHANDLER(SaveMap)
{ {
CMapWriter writer; CMapWriter writer;
writer.SaveMap(CStr(L"maps/scenarios/" + msg->filename), writer.SaveMap(CStr(L"maps/scenarios/" + *msg->filename),
g_Game->GetWorld()->GetTerrain(), g_Game->GetWorld()->GetUnitManager(), g_Game->GetWorld()->GetTerrain(), g_Game->GetWorld()->GetUnitManager(),
&g_LightEnv, g_Game->GetView()->GetCamera()); &g_LightEnv, g_Game->GetView()->GetCamera());
} }

View File

@ -25,11 +25,12 @@ namespace AtlasMessage {
static bool SortObjectsList(const sObjectsListItem& a, const sObjectsListItem& b) static bool SortObjectsList(const sObjectsListItem& a, const sObjectsListItem& b)
{ {
return a.name < b.name; return wcscmp(a.name.c_str(), b.name.c_str()) < 0;
} }
QUERYHANDLER(GetObjectsList) QUERYHANDLER(GetObjectsList)
{ {
std::vector<sObjectsListItem> objects;
{ {
std::vector<CStrW> names; std::vector<CStrW> names;
g_EntityTemplateCollection.getBaseEntityNames(names); g_EntityTemplateCollection.getBaseEntityNames(names);
@ -40,7 +41,7 @@ QUERYHANDLER(GetObjectsList)
e.id = L"(e) " + *it; e.id = L"(e) " + *it;
e.name = *it; //baseent->m_Tag e.name = *it; //baseent->m_Tag
e.type = 0; e.type = 0;
msg->objects.push_back(e); objects.push_back(e);
} }
} }
@ -54,10 +55,11 @@ QUERYHANDLER(GetObjectsList)
e.id = L"(n) " + CStrW(*it); e.id = L"(n) " + CStrW(*it);
e.name = CStrW(*it).AfterFirst(/*L"props/"*/ L"actors/"); e.name = CStrW(*it).AfterFirst(/*L"props/"*/ L"actors/");
e.type = 1; e.type = 1;
msg->objects.push_back(e); objects.push_back(e);
} }
} }
std::sort(msg->objects.begin(), msg->objects.end(), SortObjectsList); std::sort(objects.begin(), objects.end(), SortObjectsList);
msg->objects = objects;
} }
@ -98,7 +100,7 @@ void AtlasRenderSelection()
MESSAGEHANDLER(SetSelectionPreview) MESSAGEHANDLER(SetSelectionPreview)
{ {
g_Selection = msg->ids; g_Selection = *msg->ids;
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -152,7 +154,7 @@ static bool ParseObjectName(const CStrW& obj, bool& isEntity, CStrW& name)
MESSAGEHANDLER(ObjectPreview) MESSAGEHANDLER(ObjectPreview)
{ {
if (msg->id != g_PreviewUnitID) if (*msg->id != g_PreviewUnitID)
{ {
// Delete old unit // Delete old unit
if (g_PreviewUnit) if (g_PreviewUnit)
@ -164,7 +166,7 @@ MESSAGEHANDLER(ObjectPreview)
bool isEntity; bool isEntity;
CStrW name; CStrW name;
if (ParseObjectName(msg->id, isEntity, name)) if (ParseObjectName(*msg->id, isEntity, name))
{ {
std::set<CStrW> selections; // TODO: get selections from user std::set<CStrW> selections; // TODO: get selections from user
@ -186,7 +188,7 @@ MESSAGEHANDLER(ObjectPreview)
} }
} }
g_PreviewUnitID = msg->id; g_PreviewUnitID = *msg->id;
} }
if (g_PreviewUnit) if (g_PreviewUnit)
@ -198,7 +200,7 @@ MESSAGEHANDLER(ObjectPreview)
if (msg->usetarget) if (msg->usetarget)
{ {
CVector3D target; CVector3D target;
msg->target.GetWorldSpace(target, pos.Y); msg->target->GetWorldSpace(target, pos.Y);
CVector2D dir(target.X-pos.X, target.Z-pos.Z); CVector2D dir(target.X-pos.X, target.Z-pos.Z);
dir = dir.normalize(); dir = dir.normalize();
s = dir.x; s = dir.x;
@ -234,7 +236,7 @@ BEGIN_COMMAND(CreateObject)
if (d->usetarget) if (d->usetarget)
{ {
CVector3D target; CVector3D target;
d->target.GetWorldSpace(target, m_Pos.Y); d->target->GetWorldSpace(target, m_Pos.Y);
CVector2D dir(target.X-m_Pos.X, target.Z-m_Pos.Z); CVector2D dir(target.X-m_Pos.X, target.Z-m_Pos.Z);
m_Angle = atan2(dir.x, dir.y); m_Angle = atan2(dir.x, dir.y);
} }
@ -252,7 +254,7 @@ BEGIN_COMMAND(CreateObject)
{ {
bool isEntity; bool isEntity;
CStrW name; CStrW name;
if (ParseObjectName(d->id, isEntity, name)) if (ParseObjectName(*d->id, isEntity, name))
{ {
std::set<CStrW> selections; std::set<CStrW> selections;
@ -329,7 +331,7 @@ END_COMMAND(CreateObject)
QUERYHANDLER(SelectObject) QUERYHANDLER(SelectObject)
{ {
float x, y; float x, y;
msg->pos.GetScreenSpace(x, y); msg->pos->GetScreenSpace(x, y);
CVector3D rayorigin, raydir; CVector3D rayorigin, raydir;
g_Game->GetView()->GetCamera()->BuildCameraRay((int)x, (int)y, rayorigin, raydir); g_Game->GetView()->GetCamera()->BuildCameraRay((int)x, (int)y, rayorigin, raydir);
@ -434,7 +436,7 @@ BEGIN_COMMAND(RotateObject)
{ {
CVector3D& pos = unit->GetEntity()->m_position; CVector3D& pos = unit->GetEntity()->m_position;
CVector3D target; CVector3D target;
d->target.GetWorldSpace(target, pos.Y); d->target->GetWorldSpace(target, pos.Y);
CVector2D dir(target.X-pos.X, target.Z-pos.Z); CVector2D dir(target.X-pos.X, target.Z-pos.Z);
m_AngleNew = atan2(dir.x, dir.y); m_AngleNew = atan2(dir.x, dir.y);
} }
@ -453,7 +455,7 @@ BEGIN_COMMAND(RotateObject)
if (d->usetarget) if (d->usetarget)
{ {
CVector3D target; CVector3D target;
d->target.GetWorldSpace(target, pos.Y); d->target->GetWorldSpace(target, pos.Y);
CVector2D dir(target.X-pos.X, target.Z-pos.Z); CVector2D dir(target.X-pos.X, target.Z-pos.Z);
dir = dir.normalize(); dir = dir.normalize();
s = dir.x; s = dir.x;

View File

@ -20,8 +20,10 @@ namespace AtlasMessage {
QUERYHANDLER(GetTerrainGroups) QUERYHANDLER(GetTerrainGroups)
{ {
const CTextureManager::TerrainGroupMap &groups = g_TexMan.GetGroups(); const CTextureManager::TerrainGroupMap &groups = g_TexMan.GetGroups();
for (CTextureManager ::TerrainGroupMap::const_iterator it = groups.begin(); it != groups.end(); ++it) std::vector<std::wstring> groupnames;
msg->groupnames.push_back(CStrW(it->first)); for (CTextureManager::TerrainGroupMap::const_iterator it = groups.begin(); it != groups.end(); ++it)
groupnames.push_back(CStrW(it->first));
msg->groupnames = groupnames;
} }
static bool CompareTerrain(const sTerrainGroupPreview& a, const sTerrainGroupPreview& b) static bool CompareTerrain(const sTerrainGroupPreview& a, const sTerrainGroupPreview& b)
@ -31,13 +33,15 @@ static bool CompareTerrain(const sTerrainGroupPreview& a, const sTerrainGroupPre
QUERYHANDLER(GetTerrainGroupPreviews) QUERYHANDLER(GetTerrainGroupPreviews)
{ {
CTerrainGroup* group = g_TexMan.FindGroup(CStrW(msg->groupname)); std::vector<sTerrainGroupPreview> previews;
CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupname));
for (std::vector<CTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it) for (std::vector<CTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it)
{ {
msg->previews.push_back(sTerrainGroupPreview()); previews.push_back(sTerrainGroupPreview());
msg->previews.back().name = CStrW((*it)->GetTag()); previews.back().name = CStrW((*it)->GetTag());
unsigned char* buf = (unsigned char*)malloc(msg->imagewidth*msg->imageheight*3); std::vector<unsigned char> buf (msg->imagewidth*msg->imageheight*3);
// It's not good to shrink the entire texture to fit the small preview // It's not good to shrink the entire texture to fit the small preview
// window, since it's the fine details in the texture that are // window, since it's the fine details in the texture that are
@ -72,7 +76,7 @@ QUERYHANDLER(GetTerrainGroupPreviews)
// Extract the middle section (as a representative preview), // Extract the middle section (as a representative preview),
// and copy into buf // and copy into buf
unsigned char* texdata_ptr = texdata + (w*(h - msg->imageheight)/2 + (w - msg->imagewidth)/2) * 3; unsigned char* texdata_ptr = texdata + (w*(h - msg->imageheight)/2 + (w - msg->imagewidth)/2) * 3;
unsigned char* buf_ptr = buf; unsigned char* buf_ptr = &buf[0];
for (int y = 0; y < msg->imageheight; ++y) for (int y = 0; y < msg->imageheight; ++y)
{ {
memcpy(buf_ptr, texdata_ptr, msg->imagewidth*3); memcpy(buf_ptr, texdata_ptr, msg->imagewidth*3);
@ -83,11 +87,12 @@ QUERYHANDLER(GetTerrainGroupPreviews)
delete[] texdata; delete[] texdata;
} }
msg->previews.back().imagedata = buf; previews.back().imagedata = buf;
} }
// Sort the list alphabetically by name // Sort the list alphabetically by name
std::sort(msg->previews.begin(), msg->previews.end(), CompareTerrain); std::sort(previews.begin(), previews.end(), CompareTerrain);
msg->previews = previews;
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -160,12 +165,12 @@ BEGIN_COMMAND(PaintTerrain)
void Do() void Do()
{ {
d->pos.GetWorldSpace(g_CurrentBrush.m_Centre); d->pos->GetWorldSpace(g_CurrentBrush.m_Centre);
int x0, y0; int x0, y0;
g_CurrentBrush.GetBottomLeft(x0, y0); g_CurrentBrush.GetBottomLeft(x0, y0);
CTextureEntry* texentry = g_TexMan.FindTexture(CStrW(d->texture)); CTextureEntry* texentry = g_TexMan.FindTexture(CStrW(*d->texture));
if (! texentry) if (! texentry)
{ {
debug_warn("Can't find texentry"); // TODO: nicer error handling debug_warn("Can't find texentry"); // TODO: nicer error handling

View File

@ -1,6 +1,8 @@
#ifndef MESSAGEPASSER_H__ #ifndef MESSAGEPASSER_H__
#define MESSAGEPASSER_H__ #define MESSAGEPASSER_H__
#include "SharedMemory.h"
namespace AtlasMessage namespace AtlasMessage
{ {
@ -21,7 +23,7 @@ public:
extern MessagePasser* g_MessagePasser; extern MessagePasser* g_MessagePasser;
#define POST_MESSAGE(type) AtlasMessage::g_MessagePasser->Add(new AtlasMessage::m##type) #define POST_MESSAGE(type, data) AtlasMessage::g_MessagePasser->Add(SHAREABLE_NEW(AtlasMessage::m##type, data))
} }

View File

@ -55,7 +55,7 @@ MESSAGE(Screenshot,
MESSAGE(Brush, MESSAGE(Brush,
((int, width)) // number of vertices ((int, width)) // number of vertices
((int, height)) ((int, height))
((float*, data)) // width*height array, allocated with new[] (handler will delete[]) ((std::vector<float>, data)) // width*height array
); );
MESSAGE(BrushPreview, MESSAGE(BrushPreview,
@ -73,9 +73,11 @@ QUERY(GetTerrainGroups,
struct sTerrainGroupPreview struct sTerrainGroupPreview
{ {
std::wstring name; Shareable<std::wstring> name;
unsigned char* imagedata; // RGB*size*size, allocated with malloc (querier should free) Shareable<std::vector<unsigned char> > imagedata; // RGB*size*size
}; };
SHAREABLE_POD(sTerrainGroupPreview);
QUERY(GetTerrainGroupPreviews, QUERY(GetTerrainGroupPreviews,
((std::wstring, groupname)) ((std::wstring, groupname))
((int, imagewidth)) ((int, imagewidth))
@ -89,10 +91,12 @@ QUERY(GetTerrainGroupPreviews,
struct sObjectsListItem struct sObjectsListItem
{ {
std::wstring id; Shareable<std::wstring> id;
std::wstring name; Shareable<std::wstring> name;
int type; // 0 = entity, 1 = actor Shareable<int> type; // 0 = entity, 1 = actor
}; };
SHAREABLE_POD(sObjectsListItem);
QUERY(GetObjectsList, QUERY(GetObjectsList,
, // no inputs , // no inputs
((std::vector<sObjectsListItem>, objects)) ((std::vector<sObjectsListItem>, objects))

View File

@ -4,46 +4,17 @@
#define MESSAGESSETUP_NOTFIRST #define MESSAGESSETUP_NOTFIRST
#include "MessagePasser.h" #include "MessagePasser.h"
#include "SharedTypes.h"
#include "Shareable.h"
// Structures in this file are passed over the DLL boundary, so some // Structures in this file are passed over the DLL boundary, so some
// carefulness and/or luck is required... // carefulness and/or luck is required...
class wxPoint;
class CVector3D;
class CMutex; class CMutex;
namespace AtlasMessage namespace AtlasMessage
{ {
//////////////////////////////////////////////////////////////////////////
struct Position
{
Position() : type(0) { type0.x = type0.y = type0.z = 0.f; }
Position(float x_, float y_, float z_) : type(0) { type0.x = x_; type0.y = y_; type0.z = z_; }
Position(const wxPoint& pt); // (implementation in ScenarioEditor.cpp)
int type;
union {
struct { float x, y, z; } type0; // world-space coordinates
struct { int x, y; } type1; // screen-space coordinates, to be projected onto terrain
// type2: "same as previous" (e.g. for elevation-editing when the mouse hasn't moved)
};
// Constructs a position with the meaning "same as previous", which is handled
// in an unspecified way by various message handlers.
static Position Unchanged() { Position p; p.type = 2; return p; }
// Only for use in the game, not the UI.
// Implementations in Misc.cpp.
void GetWorldSpace(CVector3D& vec) const;
void GetWorldSpace(CVector3D& vec, float h) const;
void GetWorldSpace(CVector3D& vec, const CVector3D& prev) const;
void GetScreenSpace(float& x, float& y) const;
};
//////////////////////////////////////////////////////////////////////////
struct IMessage struct IMessage
{ {
virtual const char* GetName() const = 0; virtual const char* GetName() const = 0;
@ -71,8 +42,9 @@ MESSAGESTRUCT(WorldCommand)
}; };
MESSAGESTRUCT(DoCommand) MESSAGESTRUCT(DoCommand)
mDoCommand(mWorldCommand* c) : name(c->GetName()), data(c->CloneData()) {} mDoCommand(mWorldCommand* c) : name(c->GetName()), data(c->CloneData()) {}
const std::string name; const Shareable<std::string> name;
const void* data; const Shareable<void*> data;
// 'data' gets deallocated by ~cWhatever in the game thread
}; };
MESSAGESTRUCT(UndoCommand) }; MESSAGESTRUCT(UndoCommand) };
MESSAGESTRUCT(RedoCommand) }; MESSAGESTRUCT(RedoCommand) };
@ -112,7 +84,7 @@ const bool NOMERGE = false;
m##t(const d##t& d) : d##t(d) {} \ m##t(const d##t& d) : d##t(d) {} \
const char* GetName() const { return #t; } \ const char* GetName() const { return #t; } \
virtual bool IsMergeable() const { return merge; } \ virtual bool IsMergeable() const { return merge; } \
void* CloneData() const { return new d##t(*this); } \ void* CloneData() const { return SHAREABLE_NEW(d##t, (*this)); } \
private: \ private: \
const m##t& operator=(const m##t&);\ const m##t& operator=(const m##t&);\
}; };
@ -128,15 +100,15 @@ const bool NOMERGE = false;
#define B_NAME(elem) BOOST_PP_TUPLE_ELEM(2, 1, elem) #define B_NAME(elem) BOOST_PP_TUPLE_ELEM(2, 1, elem)
#define B_CONSTRUCTORARGS(r, data, n, elem) BOOST_PP_COMMA_IF(n) B_TYPE(elem) BOOST_PP_CAT(B_NAME(elem),_) #define B_CONSTRUCTORARGS(r, data, n, elem) BOOST_PP_COMMA_IF(n) B_TYPE(elem) BOOST_PP_CAT(B_NAME(elem),_)
#define B_CONSTRUCTORINIT(r, data, n, elem) BOOST_PP_COMMA_IF(n) B_NAME(elem)(BOOST_PP_CAT(B_NAME(elem),_)) #define B_CONSTRUCTORINIT(r, data, n, elem) BOOST_PP_COMMA_IF(n) B_NAME(elem)(BOOST_PP_CAT(B_NAME(elem),_))
#define B_CONSTMEMBERS(r, data, n, elem) const B_TYPE(elem) B_NAME(elem); #define B_CONSTMEMBERS(r, data, n, elem) const Shareable< B_TYPE(elem) > B_NAME(elem);
#define B_MEMBERS(r, data, n, elem) B_TYPE(elem) B_NAME(elem); #define B_MEMBERS(r, data, n, elem) Shareable< B_TYPE(elem) > B_NAME(elem);
/* For each message type, generate something roughly like: /* For each message type, generate something roughly like:
struct mBlah : public IMessage { struct mBlah : public IMessage {
const char* GetName() const { return "Blah"; } const char* GetName() const { return "Blah"; }
mBlah(int in0_, bool in1_) : in0(in0_), in1(in1_) {} mBlah(int in0_, bool in1_) : in0(in0_), in1(in1_) {}
const int in0; const Shareable<int> in0;
const bool in1; const Shareable<bool> in1;
} }
*/ */

View File

@ -0,0 +1,277 @@
#ifndef SHAREABLE_H__
#define SHAREABLE_H__
/*
The Atlas UI DLL needs to share information with the game EXE. It's most
convenient if they can pass STL objects, like std::wstring and std::vector;
but that causes problems if the DLL and EXE were not compiled in exactly
the same way.
So, the Shareable<T> class is used to make things a bit safer:
Simple types (primitives, POD structs, etc) are passed as normal.
std::string is converted to an array, using a shared (and thread-safe) memory
allocation function so that it works when the DLL and EXE use different heaps.
std::vector is done the same, though its element type must be Shareable too.
This ought to protect against:
* Different heaps (msvcr71 vs msvcr80, debug vs release, etc).
* Different STL class layout.
It doesn't protect against:
* Different data type sizes/ranges.
* Different packing in our structs. (But they're very simple structs,
only storing size_t and pointer values.)
* Vtable layout - this code doesn't actually care, but the rest of Atlas does.
Usage should be fairly transparent - conversions from T to Shareable<T> are
automatic, and the opposite is automatic for primitive types.
For POD structs, use operator-> to access members (e.g. "msg->sharedstruct->value").
For more complex things (strings, vectors), use the unary operator* to get back
an STL object (e.g. "std::string s = *msg->sharedstring").
(That conversion to an STL object is potentially expensive, so
Shareable<string>.c_str() and Shareable<vector>.GetBuffer/GetSize() can be used
if that's all you need.)
The supported list of primitive types is below (SHAREABLE_PRIMITIVE).
Structs are made shareable by manually ensuring that all their members are
shareable (i.e. primitives, PODs, Shareable<string>s, etc) and writing
SHAREABLE_POD(StructName);
after their definition.
*/
#include "SharedMemory.h"
// We want to use placement new, which breaks when compiling Debug configurations
// in the game and in wx, and they both need different workarounds.
// (Duplicated in SharedMemory.h)
#ifdef new
# define SHAREABLE_USED_NOMMGR
# ifdef __WXWINDOWS__
# undef new
# else
# include "nommgr.h"
# endif
#endif
namespace AtlasMessage
{
// By default, things are not shareable
template <typename T> class Shareable
{
public:
Shareable();
enum { TypeIsShareable = 0 };
};
// Primitive types just need a very simple wrapper
#define SHAREABLE_PRIMITIVE(T) \
template<> class Shareable<T> \
{ \
T m; \
public: \
enum { TypeIsShareable = 1 }; \
Shareable() {} \
Shareable(T const& rhs) { m = rhs; } \
operator const T() const { return m; } \
const T _Unwrap() const { return m; } \
}
SHAREABLE_PRIMITIVE(unsigned char);
SHAREABLE_PRIMITIVE(int);
SHAREABLE_PRIMITIVE(bool);
SHAREABLE_PRIMITIVE(float);
SHAREABLE_PRIMITIVE(void*);
#undef SHAREABLE_PRIMITIVE
// POD types are similar to primitives, but with operator->
#define SHAREABLE_POD(T) \
template<> class Shareable<T> \
{ \
T m; \
public: \
enum { TypeIsShareable = 1 }; \
Shareable() {} \
Shareable(T const& rhs) { m = rhs; } \
const T* operator->() const { return &m; } \
operator const T() const { return m; } \
const T _Unwrap() const { return m; } \
}
// Shareable containers must have shareable contents - but it's easy to forget
// to declare them, so make sure the errors are almost readable, like:
// "use of undefined type 'REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE<T,__formal>
// with [ T=AtlasMessage::sTerrainGroupPreview, __formal=false ]"
//
// (Implementation based on boost/static_assert)
template <typename T, bool> struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE;
template <typename T> struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE<T, true>{};
template<int x> struct static_assert_test{};
#define ASSERT_TYPE_IS_SHAREABLE(T) typedef static_assert_test< \
sizeof(REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE< T, (bool)(Shareable<T>::TypeIsShareable) >)> \
static_assert_typedef_
// Shareable strings:
template<typename C> class Shareable< std::basic_string<C> >
{
typedef std::basic_string<C> wrapped_type;
const static C null = 0; // for null strings of the right type
C* buf; // null-terminated string (perhaps with embedded nulls)
size_t length; // size of buf (including null)
public:
enum { TypeIsShareable = 1 };
Shareable() : buf(NULL), length(0) {}
Shareable(const wrapped_type& rhs)
{
length = rhs.length()+1;
buf = (C*)ShareableMallocFptr(sizeof(C)*length);
memcpy(buf, rhs.c_str(), sizeof(C)*length);
}
~Shareable()
{
ShareableFreeFptr(buf);
}
Shareable<wrapped_type>& operator=(const Shareable<wrapped_type>& rhs)
{
if (&rhs == this)
return *this;
ShareableFreeFptr(buf);
length = rhs.length;
buf = (C*)ShareableMallocFptr(sizeof(C)*length);
memcpy(buf, rhs.buf, sizeof(C)*length);
return *this;
}
Shareable(const Shareable<wrapped_type>& rhs)
: buf(NULL), length(0)
{
*this = rhs;
}
const wrapped_type _Unwrap() const
{
return buf ? wrapped_type(buf, buf+length-1) : wrapped_type();
}
const wrapped_type operator*() const
{
return _Unwrap();
}
// Minor optimisation for code that just wants to access the characters,
// without constructing a new std::basic_string then calling c_str on that
const C* c_str() const
{
return buf ? buf : &null;
}
};
// Shareable vectors:
template<typename E> class Shareable<std::vector<E> >
{
ASSERT_TYPE_IS_SHAREABLE(E);
typedef std::vector<E> wrapped_type;
typedef Shareable<E> element_type;
element_type* array;
size_t size;
// Since we're allocating with malloc (roughly), but storing non-trivial
// objects, we have to allocate with placement new and call destructors
// manually. (At least the objects are usually just other Shareables, so it's
// reasonably safe to assume there's no exceptions or other confusingness.)
void Unalloc()
{
if (array == NULL)
return;
for (size_t i = 0; i < size; ++i)
array[i].~element_type();
ShareableFreeFptr(array);
array = NULL;
size = 0;
}
public:
enum { TypeIsShareable = 1 };
Shareable() : array(NULL), size(0) {}
Shareable(const wrapped_type& rhs)
{
size = rhs.size();
array = static_cast<element_type*> (ShareableMallocFptr( sizeof(element_type)*size ));
for (size_t i = 0; i < size; ++i)
new (&array[i]) element_type (rhs[i]);
}
~Shareable()
{
Unalloc();
}
Shareable<wrapped_type>& operator=(const Shareable<wrapped_type>& rhs)
{
if (&rhs == this)
return *this;
Unalloc();
size = rhs.size;
array = static_cast<element_type*> (ShareableMallocFptr( sizeof(element_type)*size ));
for (size_t i = 0; i < size; ++i)
new (&array[i]) element_type (rhs.array[i]);
return *this;
}
Shareable(const Shareable<wrapped_type>& rhs)
: array(NULL), size(0)
{
*this = rhs;
}
const wrapped_type _Unwrap() const
{
wrapped_type r;
r.reserve(size);
for (size_t i = 0; i < size; ++i)
r.push_back(array[i]._Unwrap());
return r;
}
const wrapped_type operator*() const
{
return _Unwrap();
}
// Minor optimisation for code that just wants to access the buffer,
// without constructing a new std::vector
const element_type* GetBuffer() const
{
return array;
}
size_t GetSize() const
{
return size;
}
};
}
#ifdef SHAREABLE_USED_NOMMGR
# ifdef __WXWINDOWS__ // TODO: portability to non-Windows wx
# define new new( _NORMAL_BLOCK, __FILE__, __LINE__)
# else
# include "mmgr.h"
# endif
# undef SHAREABLE_USED_NOMMGR
#endif
#endif // SHAREABLE_H__

View File

@ -0,0 +1,64 @@
#ifndef SHAREDMEMORY_H__
#define SHAREDMEMORY_H__
// We want to use placement new, which breaks when compiling Debug configurations
// in the game and in wx, and they both need different workarounds.
// (Duplicated in Shareable.h)
#ifdef new
# define SHAREABLE_USED_NOMMGR
# ifdef __WXWINDOWS__
# undef new
# else
# include "nommgr.h"
# endif
#endif
namespace AtlasMessage
{
// Shared pointers need to be allocated and freed from the same heap.
// So, both sides of the Shareable interface should set these function pointers
// to point to the same function. (The game will have to dynamically load them
// from the DLL.)
extern void* (*ShareableMallocFptr) (size_t n);
extern void (*ShareableFreeFptr) (void* p);
// Implement shared new/delete on top of those
template<typename T> T* ShareableNew()
{
T* p = (T*)ShareableMallocFptr(sizeof(T));
new (p) T;
return p;
}
template<typename T> void ShareableDelete(T* p)
{
p->~T();
ShareableFreeFptr(p);
}
// Or maybe we want to use a non-default constructor
#define SHAREABLE_NEW(T, data) (new ( (T*)AtlasMessage::ShareableMallocFptr(sizeof(T)) ) T data)
}
#ifdef SHAREABLE_USED_NOMMGR
// # ifdef __WXWINDOWS__ // TODO: portability to non-Windows wx
// # define new new( _NORMAL_BLOCK, __FILE__, __LINE__)
// # else
// # include "mmgr.h"
// # endif
// Actually, we don't want to redefine 'new', because it conflicts with all users
// of SHAREABLE_NEW. So just leave it undefined, and put up with the less
// informative leak messages.
# undef SHAREABLE_USED_NOMMGR
// Oh, but we don't want other game headers to include mmgr.h again.
// So let's just cheat horribly and remove the options which cause it to
// redefine new.
# undef HAVE_VC_DEBUG_ALLOC
# define HAVE_VC_DEBUG_ALLOC 0
# undef CONFIG_USE_MMGR
# define CONFIG_USE_MMGR 0
#endif
#endif // SHAREDMEMORY_H__

View File

@ -0,0 +1,40 @@
#ifndef SHAREDTYPES_H__
#define SHAREDTYPES_H__
#include "Shareable.h"
class wxPoint;
class CVector3D;
namespace AtlasMessage
{
struct Position
{
Position() : type(0) { type0.x = type0.y = type0.z = 0.f; }
Position(float x_, float y_, float z_) : type(0) { type0.x = x_; type0.y = y_; type0.z = z_; }
Position(const wxPoint& pt); // (implementation in ScenarioEditor.cpp)
int type;
union {
struct { float x, y, z; } type0; // world-space coordinates
struct { int x, y; } type1; // screen-space coordinates, to be projected onto terrain
// type2: "same as previous" (e.g. for elevation-editing when the mouse hasn't moved)
};
// Constructs a position with the meaning "same as previous", which is handled
// in an unspecified way by various message handlers.
static Position Unchanged() { Position p; p.type = 2; return p; }
// Only for use in the game, not the UI.
// Implementations in Misc.cpp.
void GetWorldSpace(CVector3D& vec) const;
void GetWorldSpace(CVector3D& vec, float h) const;
void GetWorldSpace(CVector3D& vec, const CVector3D& prev) const;
void GetScreenSpace(float& x, float& y) const;
};
SHAREABLE_POD(Position);
}
#endif // SHAREDTYPES_H__