From d9b033c85f9a5d096468a5e88dcb09f3d4df3699 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Fri, 21 Apr 2006 03:53:10 +0000 Subject: [PATCH] # Enabled some interoperability between different compiler versions for the game and the editor DLL This was SVN commit r3790. --- .../tools/atlas/AtlasUI/Misc/DLLInterface.cpp | 48 ++- .../tools/atlas/AtlasUI/Misc/DLLInterface.h | 4 +- .../AtlasUI/ScenarioEditor/ScenarioEditor.cpp | 40 +-- .../ScenarioEditor/Sections/Map/Map.cpp | 8 +- .../ScenarioEditor/Sections/Object/Object.cpp | 2 +- .../Sections/Terrain/Terrain.cpp | 13 +- .../ScenarioEditor/Tools/AlterElevation.cpp | 8 +- .../ScenarioEditor/Tools/Common/Brushes.cpp | 6 +- .../ScenarioEditor/Tools/Common/Brushes.h | 2 +- .../ScenarioEditor/Tools/Common/Tools.cpp | 12 +- .../ScenarioEditor/Tools/FlattenElevation.cpp | 8 +- .../ScenarioEditor/Tools/PaintTerrain.cpp | 6 +- .../ScenarioEditor/Tools/PlaceObject.cpp | 4 +- .../ScenarioEditor/Tools/TransformObject.cpp | 6 +- source/tools/atlas/GameInterface/Brushes.cpp | 6 +- source/tools/atlas/GameInterface/Brushes.h | 4 +- .../tools/atlas/GameInterface/CommandProc.h | 9 +- source/tools/atlas/GameInterface/GameLoop.cpp | 14 +- .../GameInterface/Handlers/BrushHandlers.cpp | 4 +- .../Handlers/CameraCtrlHandlers.cpp | 10 +- .../Handlers/CommandHandlers.cpp | 2 +- .../Handlers/ElevationHandlers.cpp | 4 +- .../GameInterface/Handlers/MapHandlers.cpp | 4 +- .../GameInterface/Handlers/ObjectHandlers.cpp | 30 +- .../Handlers/TerrainHandlers.cpp | 27 +- .../tools/atlas/GameInterface/MessagePasser.h | 4 +- source/tools/atlas/GameInterface/Messages.h | 16 +- .../tools/atlas/GameInterface/MessagesSetup.h | 48 +-- source/tools/atlas/GameInterface/Shareable.h | 277 ++++++++++++++++++ .../tools/atlas/GameInterface/SharedMemory.h | 64 ++++ .../tools/atlas/GameInterface/SharedTypes.h | 40 +++ 31 files changed, 569 insertions(+), 161 deletions(-) create mode 100644 source/tools/atlas/GameInterface/Shareable.h create mode 100644 source/tools/atlas/GameInterface/SharedMemory.h create mode 100644 source/tools/atlas/GameInterface/SharedTypes.h diff --git a/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp b/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp index f661e0137b..5d6f059ae8 100644 --- a/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp +++ b/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp @@ -14,6 +14,26 @@ #include "wx/config.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 wxString g_InitialWindowType; HINSTANCE g_Module; @@ -22,20 +42,20 @@ bool g_IsLoaded = false; BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID WXUNUSED(lpReserved)) { - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - g_Module = hModule; - return TRUE; - - case DLL_PROCESS_DETACH: - if (g_IsLoaded) + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + g_Module = hModule; + return TRUE; + + case DLL_PROCESS_DETACH: + if (g_IsLoaded) { - wxEntryCleanup(); - g_IsLoaded = false; - } - break; - } + wxEntryCleanup(); + g_IsLoaded = false; + } + break; + } return TRUE; } @@ -166,4 +186,4 @@ public: } }; -IMPLEMENT_APP_NO_MAIN(wxDLLApp) +IMPLEMENT_APP_NO_MAIN(wxDLLApp) diff --git a/source/tools/atlas/AtlasUI/Misc/DLLInterface.h b/source/tools/atlas/AtlasUI/Misc/DLLInterface.h index 587e732fd9..889561f7ee 100644 --- a/source/tools/atlas/AtlasUI/Misc/DLLInterface.h +++ b/source/tools/atlas/AtlasUI/Misc/DLLInterface.h @@ -1,8 +1,8 @@ #include -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_GLSetCurrent(void* context); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index 69d8fad98f..44080e3831 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -39,7 +39,7 @@ public: // Be careful not to send 'resize' messages to the game before we've // told it that this canvas exists if (! m_SuppressResize) - POST_MESSAGE(ResizeScreen(GetClientSize().GetWidth(), GetClientSize().GetHeight())); + POST_MESSAGE(ResizeScreen, (GetClientSize().GetWidth(), GetClientSize().GetHeight())); // TODO: fix flashing #endif // UI_ONLY } @@ -74,14 +74,14 @@ public: 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_RIGHT)) POST_MESSAGE(ScrollConstant(AtlasMessage::eScrollConstantDir::RIGHT, 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_LEFT)) POST_MESSAGE(ScrollConstant, (AtlasMessage::eScrollConstantDir::LEFT, 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_DOWN)) POST_MESSAGE(ScrollConstant, (AtlasMessage::eScrollConstantDir::BACKWARDS, speed)); } else { - POST_MESSAGE(ScrollConstant(dir, enable ? speed : 0.0f)); + POST_MESSAGE(ScrollConstant, (dir, enable ? speed : 0.0f)); } #endif // UI_ONLY return true; @@ -130,7 +130,7 @@ public: float speed = 16.f; if (wxGetKeyState(WXK_SHIFT)) speed *= 4.f; - POST_MESSAGE(SmoothZoom(speed*dir)); + POST_MESSAGE(SmoothZoom, (speed*dir)); } else evt.Skip(); @@ -210,7 +210,7 @@ public: else if (wxGetKeyState(WXK_SHIFT)) speed *= 4.f; - POST_MESSAGE(SmoothZoom(evt.GetWheelRotation() * speed / evt.GetWheelDelta())); + POST_MESSAGE(SmoothZoom, (evt.GetWheelRotation() * speed / evt.GetWheelDelta())); } else { @@ -229,8 +229,8 @@ public: switch (m_MouseState) { case NONE: break; - case SCROLL: POST_MESSAGE(Scroll(AtlasMessage::eScrollType::FROM, evt.GetPosition())); break; - case ROTATEAROUND: POST_MESSAGE(RotateAround(AtlasMessage::eRotateAroundType::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; default: wxFAIL; } m_LastMouseState = m_MouseState; @@ -240,8 +240,8 @@ public: switch (m_MouseState) { case NONE: break; - case SCROLL: POST_MESSAGE(Scroll(AtlasMessage::eScrollType::TO, evt.GetPosition())); break; - case ROTATEAROUND: POST_MESSAGE(RotateAround(AtlasMessage::eRotateAroundType::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; default: wxFAIL; } } @@ -432,17 +432,17 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent) // Send setup messages to game engine: #ifndef UI_ONLY - POST_MESSAGE(SetContext(canvas->GetContext())); + POST_MESSAGE(SetContext, (canvas->GetContext())); - POST_MESSAGE(CommandString("init")); + POST_MESSAGE(CommandString, ("init")); canvas->InitSize(); // Start with a blank map (so that the editor can assume there's always // a valid map loaded) - POST_MESSAGE(GenerateMap(9)); + POST_MESSAGE(GenerateMap, (9)); - POST_MESSAGE(CommandString("render_enable")); + POST_MESSAGE(CommandString, ("render_enable")); #endif // Set up a timer to make sure tool-updates happen frequently (in addition @@ -458,7 +458,7 @@ void ScenarioEditor::OnClose(wxCloseEvent&) SetCurrentTool(_T("")); #ifndef UI_ONLY - POST_MESSAGE(CommandString("shutdown")); + POST_MESSAGE(CommandString, ("shutdown")); #endif AtlasMessage::qExit().Post(); @@ -510,17 +510,17 @@ void ScenarioEditor::OnRedo(wxCommandEvent&) void ScenarioEditor::OnWireframe(wxCommandEvent& event) { - POST_MESSAGE(RenderStyle(event.IsChecked())); + POST_MESSAGE(RenderStyle, (event.IsChecked())); } void ScenarioEditor::OnMessageTrace(wxCommandEvent& event) { - POST_MESSAGE(MessageTrace(event.IsChecked())); + POST_MESSAGE(MessageTrace, (event.IsChecked())); } void ScenarioEditor::OnScreenshot(wxCommandEvent& WXUNUSED(event)) { - POST_MESSAGE(Screenshot(10)); + POST_MESSAGE(Screenshot, (10)); } ////////////////////////////////////////////////////////////////////////// diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp index 6816f842df..b531539736 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp @@ -25,7 +25,7 @@ static void LoadMap(void*) SetCurrentTool(_T("")); // TODO: clear the undo buffer, etc - POST_MESSAGE(LoadMap(map)); + POST_MESSAGE(LoadMap, (map)); } 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/ std::wstring map = dlg.GetFilename().c_str(); - POST_MESSAGE(SaveMap(map)); + POST_MESSAGE(SaveMap, (map)); } } static void GenerateMap(void*) { - POST_MESSAGE(GenerateMap(9)); + POST_MESSAGE(GenerateMap, (9)); } static void GenerateRMS(void* data) @@ -64,7 +64,7 @@ static void GenerateRMS(void* data) wxExecute(argv, wxEXEC_SYNC); wxFileName::SetCwd(cwd); - POST_MESSAGE(LoadMap(L"_atlasrm.pmp")); + POST_MESSAGE(LoadMap, (L"_atlasrm.pmp")); } ////////////////////////////////////////////////////////////////////////// diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp index bfbe5dc6a9..890937ce9d 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp @@ -91,7 +91,7 @@ void ObjectSidebar::OnFirstDisplay() { AtlasMessage::qGetObjectsList qry; qry.Post(); - p->m_Objects = qry.objects; + p->m_Objects = *qry.objects; SetObjectFilter(0); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp index 089a9295de..4b8f9f99bd 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp @@ -80,10 +80,16 @@ public: AtlasMessage::qGetTerrainGroupPreviews qry(m_Name.c_str(), imageWidth, imageHeight); qry.Post(); + std::vector previews = *qry.previews; + int i = 0; - for (std::vector::iterator it = qry.previews.begin(); it != qry.previews.end(); ++it) + for (std::vector::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*, 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)); wxListItem item; @@ -142,7 +148,8 @@ public: // Get the list of terrain groups from the engine AtlasMessage::qGetTerrainGroups qry; qry.Post(); - for (std::vector::iterator it = qry.groupnames.begin(); it != qry.groupnames.end(); ++it) + std::vector groupnames = *qry.groupnames; + for (std::vector::iterator it = groupnames.begin(); it != groupnames.end(); ++it) m_TerrainGroups.Add(it->c_str()); for (size_t i = 0; i < m_TerrainGroups.GetCount(); ++i) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp index 8599bc9dca..147930214b 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp @@ -27,7 +27,7 @@ public: void OnDisable() { - POST_MESSAGE(BrushPreview(false, Position())); + POST_MESSAGE(BrushPreview, (false, Position())); } @@ -49,7 +49,7 @@ public: } else if (evt.Moving()) { - POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition()))); + POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition()))); return true; } else @@ -65,7 +65,7 @@ public: { void OnEnter(AlterElevation* obj) { - POST_MESSAGE(BrushPreview(true, obj->m_Pos)); + POST_MESSAGE(BrushPreview, (true, obj->m_Pos)); } void OnLeave(AlterElevation*) @@ -84,7 +84,7 @@ public: { wxPoint pos = evt.GetPosition(); obj->m_Pos = Position(pos); - POST_MESSAGE(BrushPreview(true, obj->m_Pos)); + POST_MESSAGE(BrushPreview, (true, obj->m_Pos)); return true; } else diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp index ada22e0701..5839e7d5a0 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.cpp @@ -40,12 +40,12 @@ int Brush::GetHeight() const return GetWidth(); } -float* Brush::GetNewedData() const +std::vector Brush::GetData() const { int width = GetWidth(); int height = GetHeight(); - float* data = new float[width*height]; + std::vector data (width*height); switch (m_Shape) { @@ -200,7 +200,7 @@ void Brush::MakeActive() void Brush::Send() { if (m_IsActive) - POST_MESSAGE(Brush(GetWidth(), GetHeight(), GetNewedData())); + POST_MESSAGE(Brush, (GetWidth(), GetHeight(), GetData())); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.h index 6a56b259e6..2f333a6109 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Brushes.h @@ -16,7 +16,7 @@ public: int GetWidth() const; int GetHeight() const; - float* GetNewedData() const; // freshly allocated via new[] + std::vector GetData() const; float GetStrength() const; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp index 9420823770..f32ae2d4f0 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp @@ -111,16 +111,20 @@ WorldCommand::WorldCommand(AtlasMessage::mWorldCommand* command) WorldCommand::~WorldCommand() { + // m_Command was allocated by POST_COMMAND delete m_Command; } bool WorldCommand::Do() { if (m_AlreadyDone) - POST_MESSAGE(RedoCommand()); + POST_MESSAGE(RedoCommand, ()); 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; } return true; @@ -128,7 +132,7 @@ bool WorldCommand::Do() bool WorldCommand::Undo() { - POST_MESSAGE(UndoCommand()); + POST_MESSAGE(UndoCommand, ()); return true; } @@ -145,6 +149,6 @@ bool WorldCommand::Merge(AtlasWindowCommand* p) if (! m_Command->IsMergeable()) return false; - POST_MESSAGE(MergeCommand()); + POST_MESSAGE(MergeCommand, ()); return true; } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/FlattenElevation.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/FlattenElevation.cpp index cbc756c38e..7e89ba65bc 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/FlattenElevation.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/FlattenElevation.cpp @@ -26,7 +26,7 @@ public: void OnDisable() { - POST_MESSAGE(BrushPreview(false, Position())); + POST_MESSAGE(BrushPreview, (false, Position())); } @@ -42,7 +42,7 @@ public: } else if (evt.Moving()) { - POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition()))); + POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition()))); return true; } else @@ -58,7 +58,7 @@ public: { void OnEnter(FlattenElevation* obj) { - POST_MESSAGE(BrushPreview(true, obj->m_Pos)); + POST_MESSAGE(BrushPreview, (true, obj->m_Pos)); } void OnLeave(FlattenElevation*) @@ -77,7 +77,7 @@ public: { wxPoint pos = evt.GetPosition(); obj->m_Pos = Position(pos); - POST_MESSAGE(BrushPreview(true, obj->m_Pos)); + POST_MESSAGE(BrushPreview, (true, obj->m_Pos)); return true; } else diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp index 3210c913c3..4cfb152255 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp @@ -28,7 +28,7 @@ public: void OnDisable() { - POST_MESSAGE(BrushPreview(false, Position())); + POST_MESSAGE(BrushPreview, (false, Position())); } @@ -50,7 +50,7 @@ public: } else if (evt.Moving()) { - POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition()))); + POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition()))); return true; } else @@ -96,7 +96,7 @@ public: 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())); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PlaceObject.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PlaceObject.cpp index 5f6776001b..0f2a056ac3 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PlaceObject.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PlaceObject.cpp @@ -30,9 +30,9 @@ public: + (m_ScreenPos.type1.y-m_Target.type1.y)*(m_ScreenPos.type1.y-m_Target.type1.y); bool useTarget = (dragDistSq >= 16*16); 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 - 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) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp index 895b2c0a46..8ae7afbdb3 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp @@ -23,7 +23,7 @@ public: void OnDisable() { m_Selection.clear(); - POST_MESSAGE(SetSelectionPreview(m_Selection)); + POST_MESSAGE(SetSelectionPreview, (m_Selection)); } @@ -46,7 +46,7 @@ public: obj->m_dy = qry.offsety; SET_STATE(Dragging); } - POST_MESSAGE(SetSelectionPreview(obj->m_Selection)); + POST_MESSAGE(SetSelectionPreview, (obj->m_Selection)); ScenarioEditor::GetCommandProc().FinaliseLastCommand(); return true; } @@ -68,7 +68,7 @@ public: for (size_t i = 0; i < obj->m_Selection.size(); ++i) POST_COMMAND(DeleteObject, (obj->m_Selection[i])); obj->m_Selection.clear(); - POST_MESSAGE(SetSelectionPreview(obj->m_Selection)); + POST_MESSAGE(SetSelectionPreview, (obj->m_Selection)); return true; } else diff --git a/source/tools/atlas/GameInterface/Brushes.cpp b/source/tools/atlas/GameInterface/Brushes.cpp index 5d95f4c2c2..cd0ee4aea0 100644 --- a/source/tools/atlas/GameInterface/Brushes.cpp +++ b/source/tools/atlas/GameInterface/Brushes.cpp @@ -49,22 +49,20 @@ public: }; 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() { 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& data) { m_W = w; m_H = h; - delete[] m_Data; m_Data = data; } diff --git a/source/tools/atlas/GameInterface/Brushes.h b/source/tools/atlas/GameInterface/Brushes.h index c8ad91fdfc..ffdf7871d6 100644 --- a/source/tools/atlas/GameInterface/Brushes.h +++ b/source/tools/atlas/GameInterface/Brushes.h @@ -9,7 +9,7 @@ struct Brush Brush(); ~Brush(); - void SetData(int w, int h, const float* data); + void SetData(int w, int h, const std::vector& data); void SetRenderEnabled(bool enabled); // initial state is disabled @@ -27,7 +27,7 @@ struct Brush CVector3D m_Centre; private: TerrainOverlay* m_TerrainOverlay; // NULL if rendering is not enabled - const float* m_Data; + std::vector m_Data; }; extern Brush g_CurrentBrush; diff --git a/source/tools/atlas/GameInterface/CommandProc.h b/source/tools/atlas/GameInterface/CommandProc.h index b1a711ac22..4861d14595 100644 --- a/source/tools/atlas/GameInterface/CommandProc.h +++ b/source/tools/atlas/GameInterface/CommandProc.h @@ -2,6 +2,8 @@ #include #include +#include "SharedMemory.h" + namespace AtlasMessage { @@ -47,7 +49,8 @@ struct DataCommand : public Command // so commands can optionally override (De|C { void Destruct() {}; 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"); } }; @@ -57,8 +60,8 @@ struct DataCommand : public Command // so commands can optionally override (De|C d##t* d; \ public: \ c##t(d##t* data) : d(data) { Construct(); } \ - ~c##t() { Destruct(); delete d; } \ - static Command* Create(const void* data) { return new c##t((d##t*)data); } \ + ~c##t() { Destruct(); AtlasMessage::ShareableDelete(d); /* d was allocated by mDoCommand() */ } \ + static Command* Create(const void* data) { return new c##t ((d##t*)data); } \ virtual void Merge(Command* prev) { MergeWithSelf((c##t*)prev); } \ virtual const char* GetType() const { return #t; } diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp index aa3b54e90e..e5b8aab83a 100644 --- a/source/tools/atlas/GameInterface/GameLoop.cpp +++ b/source/tools/atlas/GameInterface/GameLoop.cpp @@ -4,6 +4,7 @@ #include "MessagePasserImpl.h" #include "Messages.h" +#include "SharedMemory.h" #include "Brushes.h" #include "Handlers/MessageHandler.h" @@ -41,6 +42,11 @@ void (*Atlas_SetMessagePasser)(MessagePasser*); void (*Atlas_GLSetCurrent)(void* context); void (*Atlas_GLSwapBuffers)(void* context); void (*Atlas_NotifyEndOfFrame)(); +namespace AtlasMessage +{ + void* (*ShareableMallocFptr)(size_t); + void (*ShareableFreeFptr)(void*); +} static MessagePasserImpl msgPasser; @@ -71,6 +77,10 @@ bool BeginAtlas(int argc, char* argv[], void* dll) GET(Atlas_GLSwapBuffers); GET(Atlas_NotifyEndOfFrame); #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 Atlas_SetMessagePasser(&msgPasser); @@ -129,7 +139,7 @@ bool BeginAtlas(int argc, char* argv[], void* dll) // construct a reference to the appropriate handler for the // given string) name += "_"; - name += static_cast(msg)->name; + name += *static_cast(msg)->name; // use 'static_cast' when casting messages, to make it clear // that something slightly dangerous is happening - we have // 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 // took ownership of it. - delete msg; + AtlasMessage::ShareableDelete(msg); } } } diff --git a/source/tools/atlas/GameInterface/Handlers/BrushHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/BrushHandlers.cpp index aa6826425d..fd9f7ffc39 100644 --- a/source/tools/atlas/GameInterface/Handlers/BrushHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/BrushHandlers.cpp @@ -8,13 +8,13 @@ namespace AtlasMessage { MESSAGEHANDLER(Brush) { - g_CurrentBrush.SetData(msg->width, msg->height, msg->data); + g_CurrentBrush.SetData(msg->width, msg->height, *msg->data); } MESSAGEHANDLER(BrushPreview) { g_CurrentBrush.SetRenderEnabled(msg->enable); - msg->pos.GetWorldSpace(g_CurrentBrush.m_Centre); + msg->pos->GetWorldSpace(g_CurrentBrush.m_Centre); } } diff --git a/source/tools/atlas/GameInterface/Handlers/CameraCtrlHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/CameraCtrlHandlers.cpp index e81cd97aa1..94190660c4 100644 --- a/source/tools/atlas/GameInterface/Handlers/CameraCtrlHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/CameraCtrlHandlers.cpp @@ -53,14 +53,14 @@ MESSAGEHANDLER(Scroll) if (msg->type == eScrollType::FROM) { - msg->pos.GetWorldSpace(targetPos); + msg->pos->GetWorldSpace(targetPos); targetDistance = (targetPos - camera.GetTranslation()).GetLength(); } else if (msg->type == eScrollType::TO) { CVector3D origin, dir; float x, y; - msg->pos.GetScreenSpace(x, y); + msg->pos->GetScreenSpace(x, y); g_Game->GetView()->GetCamera()->BuildCameraRay((int)x, (int)y, origin, dir); dir *= targetDistance; camera.Translate(targetPos - dir - origin); @@ -87,13 +87,13 @@ MESSAGEHANDLER(RotateAround) if (msg->type == eRotateAroundType::FROM) { - msg->pos.GetScreenSpace(lastX, lastY); // get mouse position - msg->pos.GetWorldSpace(focusPos); // get point on terrain under mouse + msg->pos->GetScreenSpace(lastX, lastY); // get mouse position + msg->pos->GetWorldSpace(focusPos); // get point on terrain under mouse } else if (msg->type == eRotateAroundType::TO) { 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 float rotX = 6.f * (y-lastY) / g_Renderer.GetHeight(); diff --git a/source/tools/atlas/GameInterface/Handlers/CommandHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/CommandHandlers.cpp index 927c3277dd..74d2df091c 100644 --- a/source/tools/atlas/GameInterface/Handlers/CommandHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/CommandHandlers.cpp @@ -10,7 +10,7 @@ namespace AtlasMessage { MESSAGEHANDLER(DoCommand) { 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()) { c = (it->second)(msg->data); diff --git a/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp index 7fe01e3b93..31254f35eb 100644 --- a/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp @@ -104,7 +104,7 @@ BEGIN_COMMAND(AlterElevation) } static CVector3D previousPosition; - d->pos.GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition); + d->pos->GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition); previousPosition = g_CurrentBrush.m_Centre; int x0, y0; @@ -163,7 +163,7 @@ BEGIN_COMMAND(FlattenElevation) int amount = (int)d->amount; static CVector3D previousPosition; - d->pos.GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition); + d->pos->GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition); previousPosition = g_CurrentBrush.m_Centre; int xc, yc; diff --git a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp index d92b56e7a5..e48dff4dc3 100644 --- a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -98,14 +98,14 @@ MESSAGEHANDLER(GenerateMap) MESSAGEHANDLER(LoadMap) { - InitGame(msg->filename); + InitGame(*msg->filename); StartGame(); } MESSAGEHANDLER(SaveMap) { 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_LightEnv, g_Game->GetView()->GetCamera()); } diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index e55f7286c4..8216003995 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -25,11 +25,12 @@ namespace AtlasMessage { 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) { + std::vector objects; { std::vector names; g_EntityTemplateCollection.getBaseEntityNames(names); @@ -40,7 +41,7 @@ QUERYHANDLER(GetObjectsList) e.id = L"(e) " + *it; e.name = *it; //baseent->m_Tag 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.name = CStrW(*it).AfterFirst(/*L"props/"*/ L"actors/"); 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) { - g_Selection = msg->ids; + g_Selection = *msg->ids; } ////////////////////////////////////////////////////////////////////////// @@ -152,7 +154,7 @@ static bool ParseObjectName(const CStrW& obj, bool& isEntity, CStrW& name) MESSAGEHANDLER(ObjectPreview) { - if (msg->id != g_PreviewUnitID) + if (*msg->id != g_PreviewUnitID) { // Delete old unit if (g_PreviewUnit) @@ -164,7 +166,7 @@ MESSAGEHANDLER(ObjectPreview) bool isEntity; CStrW name; - if (ParseObjectName(msg->id, isEntity, name)) + if (ParseObjectName(*msg->id, isEntity, name)) { std::set selections; // TODO: get selections from user @@ -186,7 +188,7 @@ MESSAGEHANDLER(ObjectPreview) } } - g_PreviewUnitID = msg->id; + g_PreviewUnitID = *msg->id; } if (g_PreviewUnit) @@ -198,7 +200,7 @@ MESSAGEHANDLER(ObjectPreview) if (msg->usetarget) { CVector3D target; - msg->target.GetWorldSpace(target, pos.Y); + msg->target->GetWorldSpace(target, pos.Y); CVector2D dir(target.X-pos.X, target.Z-pos.Z); dir = dir.normalize(); s = dir.x; @@ -234,7 +236,7 @@ BEGIN_COMMAND(CreateObject) if (d->usetarget) { 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); m_Angle = atan2(dir.x, dir.y); } @@ -252,7 +254,7 @@ BEGIN_COMMAND(CreateObject) { bool isEntity; CStrW name; - if (ParseObjectName(d->id, isEntity, name)) + if (ParseObjectName(*d->id, isEntity, name)) { std::set selections; @@ -329,7 +331,7 @@ END_COMMAND(CreateObject) QUERYHANDLER(SelectObject) { float x, y; - msg->pos.GetScreenSpace(x, y); + msg->pos->GetScreenSpace(x, y); CVector3D 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 target; - d->target.GetWorldSpace(target, pos.Y); + d->target->GetWorldSpace(target, pos.Y); CVector2D dir(target.X-pos.X, target.Z-pos.Z); m_AngleNew = atan2(dir.x, dir.y); } @@ -453,7 +455,7 @@ BEGIN_COMMAND(RotateObject) if (d->usetarget) { CVector3D target; - d->target.GetWorldSpace(target, pos.Y); + d->target->GetWorldSpace(target, pos.Y); CVector2D dir(target.X-pos.X, target.Z-pos.Z); dir = dir.normalize(); s = dir.x; diff --git a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp index 6006e9b53f..68d6010b42 100644 --- a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp @@ -20,8 +20,10 @@ namespace AtlasMessage { QUERYHANDLER(GetTerrainGroups) { const CTextureManager::TerrainGroupMap &groups = g_TexMan.GetGroups(); - for (CTextureManager ::TerrainGroupMap::const_iterator it = groups.begin(); it != groups.end(); ++it) - msg->groupnames.push_back(CStrW(it->first)); + std::vector groupnames; + 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) @@ -31,13 +33,15 @@ static bool CompareTerrain(const sTerrainGroupPreview& a, const sTerrainGroupPre QUERYHANDLER(GetTerrainGroupPreviews) { - CTerrainGroup* group = g_TexMan.FindGroup(CStrW(msg->groupname)); + std::vector previews; + + CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupname)); for (std::vector::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it) { - msg->previews.push_back(sTerrainGroupPreview()); - msg->previews.back().name = CStrW((*it)->GetTag()); + previews.push_back(sTerrainGroupPreview()); + previews.back().name = CStrW((*it)->GetTag()); - unsigned char* buf = (unsigned char*)malloc(msg->imagewidth*msg->imageheight*3); + std::vector buf (msg->imagewidth*msg->imageheight*3); // 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 @@ -72,7 +76,7 @@ QUERYHANDLER(GetTerrainGroupPreviews) // Extract the middle section (as a representative preview), // and copy into buf 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) { memcpy(buf_ptr, texdata_ptr, msg->imagewidth*3); @@ -83,11 +87,12 @@ QUERYHANDLER(GetTerrainGroupPreviews) delete[] texdata; } - msg->previews.back().imagedata = buf; + previews.back().imagedata = buf; } // 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() { - d->pos.GetWorldSpace(g_CurrentBrush.m_Centre); + d->pos->GetWorldSpace(g_CurrentBrush.m_Centre); int 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) { debug_warn("Can't find texentry"); // TODO: nicer error handling diff --git a/source/tools/atlas/GameInterface/MessagePasser.h b/source/tools/atlas/GameInterface/MessagePasser.h index 18fd8ae04b..b8198804ee 100644 --- a/source/tools/atlas/GameInterface/MessagePasser.h +++ b/source/tools/atlas/GameInterface/MessagePasser.h @@ -1,6 +1,8 @@ #ifndef MESSAGEPASSER_H__ #define MESSAGEPASSER_H__ +#include "SharedMemory.h" + namespace AtlasMessage { @@ -21,7 +23,7 @@ public: 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)) } diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index b12ad51c1c..12287b3b37 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -55,7 +55,7 @@ MESSAGE(Screenshot, MESSAGE(Brush, ((int, width)) // number of vertices ((int, height)) - ((float*, data)) // width*height array, allocated with new[] (handler will delete[]) + ((std::vector, data)) // width*height array ); MESSAGE(BrushPreview, @@ -73,9 +73,11 @@ QUERY(GetTerrainGroups, struct sTerrainGroupPreview { - std::wstring name; - unsigned char* imagedata; // RGB*size*size, allocated with malloc (querier should free) + Shareable name; + Shareable > imagedata; // RGB*size*size }; +SHAREABLE_POD(sTerrainGroupPreview); + QUERY(GetTerrainGroupPreviews, ((std::wstring, groupname)) ((int, imagewidth)) @@ -89,10 +91,12 @@ QUERY(GetTerrainGroupPreviews, struct sObjectsListItem { - std::wstring id; - std::wstring name; - int type; // 0 = entity, 1 = actor + Shareable id; + Shareable name; + Shareable type; // 0 = entity, 1 = actor }; +SHAREABLE_POD(sObjectsListItem); + QUERY(GetObjectsList, , // no inputs ((std::vector, objects)) diff --git a/source/tools/atlas/GameInterface/MessagesSetup.h b/source/tools/atlas/GameInterface/MessagesSetup.h index 4deaacf746..29921d1c9d 100644 --- a/source/tools/atlas/GameInterface/MessagesSetup.h +++ b/source/tools/atlas/GameInterface/MessagesSetup.h @@ -4,46 +4,17 @@ #define MESSAGESSETUP_NOTFIRST #include "MessagePasser.h" +#include "SharedTypes.h" +#include "Shareable.h" // Structures in this file are passed over the DLL boundary, so some // carefulness and/or luck is required... -class wxPoint; -class CVector3D; class CMutex; 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 { virtual const char* GetName() const = 0; @@ -71,8 +42,9 @@ MESSAGESTRUCT(WorldCommand) }; MESSAGESTRUCT(DoCommand) mDoCommand(mWorldCommand* c) : name(c->GetName()), data(c->CloneData()) {} - const std::string name; - const void* data; + const Shareable name; + const Shareable data; + // 'data' gets deallocated by ~cWhatever in the game thread }; MESSAGESTRUCT(UndoCommand) }; MESSAGESTRUCT(RedoCommand) }; @@ -112,7 +84,7 @@ const bool NOMERGE = false; m##t(const d##t& d) : d##t(d) {} \ const char* GetName() const { return #t; } \ virtual bool IsMergeable() const { return merge; } \ - void* CloneData() const { return new d##t(*this); } \ + void* CloneData() const { return SHAREABLE_NEW(d##t, (*this)); } \ private: \ 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_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_CONSTMEMBERS(r, data, n, elem) const B_TYPE(elem) B_NAME(elem); -#define B_MEMBERS(r, data, n, elem) 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) Shareable< B_TYPE(elem) > B_NAME(elem); /* For each message type, generate something roughly like: struct mBlah : public IMessage { const char* GetName() const { return "Blah"; } mBlah(int in0_, bool in1_) : in0(in0_), in1(in1_) {} - const int in0; - const bool in1; + const Shareable in0; + const Shareable in1; } */ diff --git a/source/tools/atlas/GameInterface/Shareable.h b/source/tools/atlas/GameInterface/Shareable.h new file mode 100644 index 0000000000..13ab9454ed --- /dev/null +++ b/source/tools/atlas/GameInterface/Shareable.h @@ -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 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 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.c_str() and Shareable.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, Shareables, 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 class Shareable +{ +public: + Shareable(); + enum { TypeIsShareable = 0 }; +}; + +// Primitive types just need a very simple wrapper +#define SHAREABLE_PRIMITIVE(T) \ + template<> class Shareable \ + { \ + 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 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 +// with [ T=AtlasMessage::sTerrainGroupPreview, __formal=false ]" +// +// (Implementation based on boost/static_assert) +template struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE; +template struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE{}; +template struct static_assert_test{}; +#define ASSERT_TYPE_IS_SHAREABLE(T) typedef static_assert_test< \ + sizeof(REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE< T, (bool)(Shareable::TypeIsShareable) >)> \ + static_assert_typedef_ + + +// Shareable strings: +template class Shareable< std::basic_string > +{ + typedef std::basic_string 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& operator=(const Shareable& 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& 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 class Shareable > +{ + ASSERT_TYPE_IS_SHAREABLE(E); + typedef std::vector wrapped_type; + typedef Shareable 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 (ShareableMallocFptr( sizeof(element_type)*size )); + for (size_t i = 0; i < size; ++i) + new (&array[i]) element_type (rhs[i]); + } + + ~Shareable() + { + Unalloc(); + } + + Shareable& operator=(const Shareable& rhs) + { + if (&rhs == this) + return *this; + Unalloc(); + size = rhs.size; + array = static_cast (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& 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__ diff --git a/source/tools/atlas/GameInterface/SharedMemory.h b/source/tools/atlas/GameInterface/SharedMemory.h new file mode 100644 index 0000000000..fbf7211ba5 --- /dev/null +++ b/source/tools/atlas/GameInterface/SharedMemory.h @@ -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 T* ShareableNew() +{ + T* p = (T*)ShareableMallocFptr(sizeof(T)); + new (p) T; + return p; +} +template 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__ \ No newline at end of file diff --git a/source/tools/atlas/GameInterface/SharedTypes.h b/source/tools/atlas/GameInterface/SharedTypes.h new file mode 100644 index 0000000000..a87346bbca --- /dev/null +++ b/source/tools/atlas/GameInterface/SharedTypes.h @@ -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__