diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj index 4e7f7b7c8d..20253a2065 100644 --- a/projects/openttd_vs140.vcxproj +++ b/projects/openttd_vs140.vcxproj @@ -1096,6 +1096,7 @@ + @@ -1163,6 +1164,7 @@ + diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters index fd5c07157c..11e9821ffe 100644 --- a/projects/openttd_vs140.vcxproj.filters +++ b/projects/openttd_vs140.vcxproj.filters @@ -2379,6 +2379,9 @@ Script API + + Script API + Script API @@ -2580,6 +2583,9 @@ Script API Implementation + + Script API Implementation + Script API Implementation diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj index 4ab1ad85df..7b0da9569c 100644 --- a/projects/openttd_vs141.vcxproj +++ b/projects/openttd_vs141.vcxproj @@ -1096,6 +1096,7 @@ + @@ -1163,6 +1164,7 @@ + diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters index fd5c07157c..11e9821ffe 100644 --- a/projects/openttd_vs141.vcxproj.filters +++ b/projects/openttd_vs141.vcxproj.filters @@ -2379,6 +2379,9 @@ Script API + + Script API + Script API @@ -2580,6 +2583,9 @@ Script API Implementation + + Script API Implementation + Script API Implementation diff --git a/projects/openttd_vs142.vcxproj b/projects/openttd_vs142.vcxproj index 2539fe1d5f..db497537db 100644 --- a/projects/openttd_vs142.vcxproj +++ b/projects/openttd_vs142.vcxproj @@ -1096,6 +1096,7 @@ + @@ -1163,6 +1164,7 @@ + diff --git a/projects/openttd_vs142.vcxproj.filters b/projects/openttd_vs142.vcxproj.filters index fd5c07157c..11e9821ffe 100644 --- a/projects/openttd_vs142.vcxproj.filters +++ b/projects/openttd_vs142.vcxproj.filters @@ -2379,6 +2379,9 @@ Script API + + Script API + Script API @@ -2580,6 +2583,9 @@ Script API Implementation + + Script API Implementation + Script API Implementation diff --git a/source.list b/source.list index cc9d10c998..b023b9ba4b 100644 --- a/source.list +++ b/source.list @@ -830,6 +830,7 @@ script/api/script_marine.hpp script/api/script_news.hpp script/api/script_object.hpp script/api/script_order.hpp +script/api/script_priorityqueue.hpp script/api/script_rail.hpp script/api/script_railtypelist.hpp script/api/script_road.hpp @@ -899,6 +900,7 @@ script/api/script_marine.cpp script/api/script_news.cpp script/api/script_object.cpp script/api/script_order.cpp +script/api/script_priorityqueue.cpp script/api/script_rail.cpp script/api/script_railtypelist.cpp script/api/script_road.cpp diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp index a4d06763fa..49f8cff64d 100644 --- a/src/ai/ai_instance.cpp +++ b/src/ai/ai_instance.cpp @@ -57,6 +57,7 @@ #include "../script/api/ai/ai_map.hpp.sq" #include "../script/api/ai/ai_marine.hpp.sq" #include "../script/api/ai/ai_order.hpp.sq" +#include "../script/api/ai/ai_priorityqueue.hpp.sq" #include "../script/api/ai/ai_rail.hpp.sq" #include "../script/api/ai/ai_railtypelist.hpp.sq" #include "../script/api/ai/ai_road.hpp.sq" @@ -164,6 +165,7 @@ void AIInstance::RegisterAPI() SQAIMap_Register(this->engine); SQAIMarine_Register(this->engine); SQAIOrder_Register(this->engine); + SQAIPriorityQueue_Register(this->engine); SQAIRail_Register(this->engine); SQAIRailTypeList_Register(this->engine); SQAIRoad_Register(this->engine); diff --git a/src/game/game_instance.cpp b/src/game/game_instance.cpp index 4424c801c6..47d51e28ee 100644 --- a/src/game/game_instance.cpp +++ b/src/game/game_instance.cpp @@ -59,6 +59,7 @@ #include "../script/api/game/game_marine.hpp.sq" #include "../script/api/game/game_news.hpp.sq" #include "../script/api/game/game_order.hpp.sq" +#include "../script/api/game/game_priorityqueue.hpp.sq" #include "../script/api/game/game_rail.hpp.sq" #include "../script/api/game/game_railtypelist.hpp.sq" #include "../script/api/game/game_road.hpp.sq" @@ -173,6 +174,7 @@ void GameInstance::RegisterAPI() SQGSMarine_Register(this->engine); SQGSNews_Register(this->engine); SQGSOrder_Register(this->engine); + SQGSPriorityQueue_Register(this->engine); SQGSRail_Register(this->engine); SQGSRailTypeList_Register(this->engine); SQGSRoad_Register(this->engine); diff --git a/src/script/api/ai/ai_priorityqueue.hpp.sq b/src/script/api/ai/ai_priorityqueue.hpp.sq new file mode 100644 index 0000000000..6e5fcf82e9 --- /dev/null +++ b/src/script/api/ai/ai_priorityqueue.hpp.sq @@ -0,0 +1,31 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "../script_priorityqueue.hpp" +#include "../template/template_priorityqueue.hpp.sq" + + +template <> const char *GetClassName() { return "AIPriorityQueue"; } + +void SQAIPriorityQueue_Register(Squirrel *engine) +{ + DefSQClass SQAIPriorityQueue("AIPriorityQueue"); + SQAIPriorityQueue.PreRegister(engine); + SQAIPriorityQueue.AddConstructor(engine, "x"); + + SQAIPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Insert, "Insert"); + SQAIPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Pop, "Pop"); + SQAIPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Peek, "Peek"); + SQAIPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Exists, "Exists"); + SQAIPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Clear, "Clear"); + SQAIPriorityQueue.DefSQMethod(engine, &ScriptPriorityQueue::IsEmpty, "IsEmpty", 1, "x"); + SQAIPriorityQueue.DefSQMethod(engine, &ScriptPriorityQueue::Count, "Count", 1, "x"); + + SQAIPriorityQueue.PostRegister(engine); +} diff --git a/src/script/api/ai_changelog.hpp b/src/script/api/ai_changelog.hpp index bcb7df67f2..a579bb27bc 100644 --- a/src/script/api/ai_changelog.hpp +++ b/src/script/api/ai_changelog.hpp @@ -17,6 +17,9 @@ * * This version is not yet released. The following changes are not set in stone yet. * + * API additions: + * \li AIPriorityQueue + * * \b 1.10.0 * * API additions: diff --git a/src/script/api/game/game_priorityqueue.hpp.sq b/src/script/api/game/game_priorityqueue.hpp.sq new file mode 100644 index 0000000000..fade5e2a1e --- /dev/null +++ b/src/script/api/game/game_priorityqueue.hpp.sq @@ -0,0 +1,31 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "../script_priorityqueue.hpp" +#include "../template/template_priorityqueue.hpp.sq" + + +template <> const char *GetClassName() { return "GSPriorityQueue"; } + +void SQGSPriorityQueue_Register(Squirrel *engine) +{ + DefSQClass SQGSPriorityQueue("GSPriorityQueue"); + SQGSPriorityQueue.PreRegister(engine); + SQGSPriorityQueue.AddConstructor(engine, "x"); + + SQGSPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Insert, "Insert"); + SQGSPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Pop, "Pop"); + SQGSPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Peek, "Peek"); + SQGSPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Exists, "Exists"); + SQGSPriorityQueue.DefSQAdvancedMethod(engine, &ScriptPriorityQueue::Clear, "Clear"); + SQGSPriorityQueue.DefSQMethod(engine, &ScriptPriorityQueue::IsEmpty, "IsEmpty", 1, "x"); + SQGSPriorityQueue.DefSQMethod(engine, &ScriptPriorityQueue::Count, "Count", 1, "x"); + + SQGSPriorityQueue.PostRegister(engine); +} diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index 69c4d971a5..fc917a0120 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -24,6 +24,7 @@ * \li GSStoryPage::MakePushButtonReference * \li GSStoryPage::MakeTileButtonReference * \li GSStoryPage::MakeVehicleButtonReference + * \li GSPriorityQueue * * \b 1.10.0 * diff --git a/src/script/api/script_priorityqueue.cpp b/src/script/api/script_priorityqueue.cpp new file mode 100644 index 0000000000..3130084126 --- /dev/null +++ b/src/script/api/script_priorityqueue.cpp @@ -0,0 +1,107 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file script_priorityqueue.cpp Implementation of ScriptPriorityQueue. */ + +#include "../../stdafx.h" +#include "script_priorityqueue.hpp" +#include "script_error.hpp" +#include "../squirrel_helper.hpp" +#include "../script_instance.hpp" +#include "../../debug.h" +#include + +#include "../../safeguards.h" + + +static bool operator==(const ScriptPriorityQueue::PriorityItem &lhs, const HSQOBJECT &rhs) +{ + return lhs.second._type == rhs._type && lhs.second._unVal.raw == rhs._unVal.raw; +} + + +ScriptPriorityQueue::~ScriptPriorityQueue() +{ + /* Release reference to stored objects. */ + auto inst = ScriptObject::GetActiveInstance(); + if (!inst->InShutdown()) { + for (auto &i : this->queue) inst->ReleaseSQObject(const_cast(&i.second)); + } +} + +SQInteger ScriptPriorityQueue::Insert(HSQUIRRELVM vm) +{ + HSQOBJECT item; + int64 priority; + sq_resetobject(&item); + sq_getstackobj(vm, 2, &item); + sq_getinteger(vm, 3, &priority); + + sq_addref(vm, &item); // Keep object alive. + + this->queue.emplace_back(priority, item); + std::push_heap(this->queue.begin(), this->queue.end(), this->comp); + + return SQConvert::Return(vm, true); +} + +SQInteger ScriptPriorityQueue::Pop(HSQUIRRELVM vm) +{ + if (this->IsEmpty()) { + ScriptObject::SetLastError(ScriptError::ERR_PRECONDITION_FAILED); + sq_pushnull(vm); + return 1; + } + + HSQOBJECT item = this->queue.front().second; + std::pop_heap(this->queue.begin(), this->queue.end(), this->comp); + this->queue.pop_back(); + + /* Store the object on the Squirrel stack before releasing it to make sure the ref count can't drop to zero. */ + auto ret = SQConvert::Return(vm, item); + sq_release(vm, &item); + return ret; +} + +SQInteger ScriptPriorityQueue::Peek(HSQUIRRELVM vm) +{ + if (this->IsEmpty()) { + ScriptObject::SetLastError(ScriptError::ERR_PRECONDITION_FAILED); + sq_pushnull(vm); + return 1; + } + + return SQConvert::Return(vm, this->queue.front().second); +} + +SQInteger ScriptPriorityQueue::Exists(HSQUIRRELVM vm) +{ + HSQOBJECT item; + sq_resetobject(&item); + sq_getstackobj(vm, 2, &item); + + return SQConvert::Return(vm, std::find(this->queue.cbegin(), this->queue.cend(), item) != this->queue.cend()); +} + +SQInteger ScriptPriorityQueue::Clear(HSQUIRRELVM vm) +{ + /* Release reference to stored objects. */ + for (auto &i : this->queue) sq_release(vm, const_cast(&i.second)); + this->queue.clear(); + + return 0; +} + +bool ScriptPriorityQueue::IsEmpty() +{ + return this->queue.empty(); +} + +SQInteger ScriptPriorityQueue::Count() +{ + return (SQInteger)this->queue.size(); +} diff --git a/src/script/api/script_priorityqueue.hpp b/src/script/api/script_priorityqueue.hpp new file mode 100644 index 0000000000..5f8718e424 --- /dev/null +++ b/src/script/api/script_priorityqueue.hpp @@ -0,0 +1,94 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file script_priorityqueue.hpp A queue that keeps a list of items sorted by a priority. */ +/** @defgroup ScriptPriorityQueue Classes that create a priority queue of items. */ + +#ifndef SCRIPT_PRIORITYQUEUE_HPP +#define SCRIPT_PRIORITYQUEUE_HPP + +#include "script_object.hpp" +#include +#include + +/** + * Class that creates a queue which keeps its items ordered by an item priority. + * @api ai game + */ +class ScriptPriorityQueue : public ScriptObject { +public: + typedef std::pair PriorityItem; +private: + struct PriorityComparator { + bool operator()(const PriorityItem &lhs, const PriorityItem &rhs) const noexcept + { + return lhs.first > rhs.first; + } + }; + + PriorityComparator comp; + std::vector queue; ///< The priority list + +public: + ~ScriptPriorityQueue(); + +#ifdef DOXYGEN_API + /** + * Add a single item to the queue. + * @param item The item to add. Can be any Squirrel type. Should be unique, otherwise it is ignored. + * @param priority The priority to assign the item. + * @return True if the item was inserted, false if it was already in the queue. + */ + bool Insert(void *item, int64 priority); + + /** + * Remove and return the item with the lowest priority. + * @return The item with the lowest priority, removed from the queue. Returns null on an empty queue. + * @pre !IsEmpty() + */ + void *Pop(); + + /** + * Get the item with the lowest priority, keeping it in the queue. + * @return The item with the lowest priority. Returns null on an empty queue. + * @pre !IsEmpty() + */ + void *Peek(); + + /** + * Check if an items is already included in the queue. + * @return true if the items is already in the queue. + * @note Performance is O(n), use only when absolutely required. + */ + bool Exists(void *item); + + /** + * Clear the queue, making Count() returning 0 and IsEmpty() returning true. + */ + void Clear(); +#else + SQInteger Insert(HSQUIRRELVM vm); + SQInteger Pop(HSQUIRRELVM vm); + SQInteger Peek(HSQUIRRELVM vm); + SQInteger Exists(HSQUIRRELVM vm); + SQInteger Clear(HSQUIRRELVM vm); +#endif + + /** + * Check if the queue is empty. + * @return true if the queue is empty. + */ + bool IsEmpty(); + + /** + * Returns the amount of items in the queue. + * @return amount of items in the queue. + */ + SQInteger Count(); +}; + +#endif /* SCRIPT_PRIORITYQUEUE_HPP */ diff --git a/src/script/api/template/template_priorityqueue.hpp.sq b/src/script/api/template/template_priorityqueue.hpp.sq new file mode 100644 index 0000000000..14c63324fb --- /dev/null +++ b/src/script/api/template/template_priorityqueue.hpp.sq @@ -0,0 +1,19 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "../script_priorityqueue.hpp" + +namespace SQConvert { + /* Allow ScriptPriorityQueue to be used as Squirrel parameter */ + template <> inline ScriptPriorityQueue *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (ScriptPriorityQueue *)instance; } + template <> inline ScriptPriorityQueue &GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptPriorityQueue *)instance; } + template <> inline const ScriptPriorityQueue *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (ScriptPriorityQueue *)instance; } + template <> inline const ScriptPriorityQueue &GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(ScriptPriorityQueue *)instance; } + template <> inline int Return(HSQUIRRELVM vm, ScriptPriorityQueue *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "PriorityQueue", res, nullptr, DefSQDestructorCallback, true); return 1; } +} // namespace SQConvert diff --git a/src/script/script_instance.cpp b/src/script/script_instance.cpp index a38a315ffc..81283cb772 100644 --- a/src/script/script_instance.cpp +++ b/src/script/script_instance.cpp @@ -58,6 +58,7 @@ ScriptInstance::ScriptInstance(const char *APIName) : is_save_data_on_stack(false), suspend(0), is_paused(false), + in_shutdown(false), callback(nullptr) { this->storage = new ScriptStorage(); @@ -136,6 +137,7 @@ bool ScriptInstance::LoadCompatibilityScripts(const char *api_version, Subdirect ScriptInstance::~ScriptInstance() { ScriptObject::ActiveInstance active(this); + this->in_shutdown = true; if (instance != nullptr) this->engine->ReleaseObject(this->instance); if (engine != nullptr) delete this->engine; @@ -154,6 +156,7 @@ void ScriptInstance::Died() { DEBUG(script, 0, "The script died unexpectedly."); this->is_dead = true; + this->in_shutdown = true; this->last_allocated_memory = this->GetAllocatedMemory(); // Update cache @@ -718,3 +721,8 @@ size_t ScriptInstance::GetAllocatedMemory() const if (this->engine == nullptr) return this->last_allocated_memory; return this->engine->GetAllocatedMemory(); } + +void ScriptInstance::ReleaseSQObject(HSQOBJECT *obj) +{ + if (!this->in_shutdown) this->engine->ReleaseObject(obj); +} diff --git a/src/script/script_instance.hpp b/src/script/script_instance.hpp index e5024c80bc..c097e0eb7b 100644 --- a/src/script/script_instance.hpp +++ b/src/script/script_instance.hpp @@ -200,6 +200,17 @@ public: size_t GetAllocatedMemory() const; + /** + * Indicate whether this instance is currently being destroyed. + */ + inline bool InShutdown() const { return this->in_shutdown; } + + /** + * Decrease the ref count of a squirrel object. + * @param obj The object to release. + **/ + void ReleaseSQObject(HSQOBJECT *obj); + protected: class Squirrel *engine; ///< A wrapper around the squirrel vm. const char *versionAPI; ///< Current API used by this script. @@ -242,6 +253,7 @@ private: bool is_save_data_on_stack; ///< Is the save data still on the squirrel stack? int suspend; ///< The amount of ticks to suspend this script before it's allowed to continue. bool is_paused; ///< Is the script paused? (a paused script will not be executed until unpaused) + bool in_shutdown; ///< Is this instance currently being destructed? Script_SuspendCallbackProc *callback; ///< Callback that should be called in the next tick the script runs. size_t last_allocated_memory; ///< Last known allocated memory value (for display for crashed scripts)