2
* Copyright (C) 2002-2025 by the Widelands Development Team
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License
6
* as published by the Free Software Foundation; either version 2
7
* of the License, or (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, see <https://www.gnu.org/licenses/>.
19
#include "logic/cmd_queue.h"
21
#include "base/macros.h"
22
#include "base/wexception.h"
23
#include "io/fileread.h"
24
#include "io/filewrite.h"
25
#include "logic/game.h"
26
#include "logic/game_data_error.h"
27
#include "logic/map_objects/tribes/worker.h"
28
#include "logic/player.h"
29
#include "logic/playercommand.h"
36
CmdQueue::CmdQueue(Game& game)
39
cmds_(kCommandQueueBucketSize, std::priority_queue<CmdItem>()) {
42
CmdQueue::~CmdQueue() {
47
* flushs all commands from the queue. Needed for
48
* game loading (while in game)
50
// TODO(unknown): ...but game loading while in game is not possible!
51
// Note: Order of destruction of Items is not guaranteed
52
void CmdQueue::flush() {
54
while ((ncmds_ != 0u) && cbucket < kCommandQueueBucketSize) {
55
std::priority_queue<CmdItem>& current_cmds = cmds_[cbucket];
57
while (!current_cmds.empty()) {
58
Command* cmd = current_cmds.top().cmd;
70
Insert a new command into the queue; it will be executed at the given time
73
void CmdQueue::enqueue(Command* const cmd) {
77
if (upcast(PlayerCommand, plcmd, cmd)) {
78
ci.category = cat_playercommand;
79
ci.serial = plcmd->cmdserial();
80
} else if (dynamic_cast<GameLogicCommand*>(cmd) != nullptr) {
81
ci.category = cat_gamelogic;
82
ci.serial = nextserial_++;
84
// the order of non-gamelogic commands matters only with respect to
85
// gamelogic commands; the order of non-gamelogic commands wrt other
86
// non-gamelogic commands shouldn't matter, so we can assign a
87
// constant serial number.
88
ci.category = cat_nongamelogic;
92
assert(cmd->duetime() >= game_.get_gametime());
93
cmds_[cmd->duetime().get() % kCommandQueueBucketSize].push(ci);
97
void CmdQueue::run_queue(const Duration& interval, Time& game_time_var) {
98
const Time final_time = game_time_var + interval;
100
while (game_time_var < final_time) {
101
std::priority_queue<CmdItem>& current_cmds =
102
cmds_[game_time_var.get() % kCommandQueueBucketSize];
104
while (!current_cmds.empty()) {
105
Command& c = *current_cmds.top().cmd;
106
if (game_time_var < c.duetime()) {
112
assert(game_time_var == c.duetime());
114
if (dynamic_cast<GameLogicCommand*>(&c) != nullptr) {
115
StreamWrite& ss = game_.syncstream();
116
ss.unsigned_8(SyncEntry::kRunQueue);
117
ss.unsigned_32(c.duetime().get());
118
ss.unsigned_32(static_cast<uint32_t>(c.id()));
125
game_time_var.increment();
128
assert(final_time == game_time_var);
131
constexpr uint16_t kCurrentPacketVersion = 1;
134
* Write variables from the base command to a file.
136
* \note This function must be called by deriving objects that override it.
138
void GameLogicCommand::write(FileWrite& fw,
140
EditorGameBase& egbase,
144
MapObjectSaver& /* mos */) {
145
fw.unsigned_16(kCurrentPacketVersion);
148
assert(egbase.get_gametime() <= duetime());
153
* Read variables for the base command from a file.
155
* \note This function must be called by deriving objects that override it.
157
void GameLogicCommand::read(FileRead& fr, EditorGameBase& egbase, MapObjectLoader& /* mol */) {
159
uint16_t const packet_version = fr.unsigned_16();
160
if (packet_version == kCurrentPacketVersion) {
161
set_duetime(Time(fr));
162
const Time& gametime = egbase.get_gametime();
163
if (duetime() < gametime) {
164
throw GameDataError("duetime (%u) < gametime (%u)", duetime().get(), gametime.get());
167
throw UnhandledVersionError("GameLogicCommand", packet_version, kCurrentPacketVersion);
169
} catch (const WException& e) {
170
throw GameDataError("game logic: %s", e.what());
173
} // namespace Widelands