widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #13726
[Merge] lp:~widelands-dev/widelands/congestion into lp:widelands
ypopezios has proposed merging lp:~widelands-dev/widelands/congestion into lp:widelands.
Commit message:
For windows testing.
Requested reviews:
Widelands Developers (widelands-dev)
Related bugs:
Bug #1535115 in widelands: "ai roads getting jammed a lot"
https://bugs.launchpad.net/widelands/+bug/1535115
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/congestion/+merge/348132
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/congestion into lp:widelands.
=== modified file 'src/economy/flag.cc'
--- src/economy/flag.cc 2018-04-07 16:59:00 +0000
+++ src/economy/flag.cc 2018-06-17 23:13:32 +0000
@@ -312,6 +312,22 @@
}
/**
+ * Called by workers wanting to drop a ware to their building's flag.
+ * \return true/allow on low congestion-risk.
+*/
+bool Flag::has_capacity_for_ware(WareInstance& ware) const {
+ // avoid iteration for the easy cases
+ if (ware_filled_ < ware_capacity_ - 2) return true;
+ if (ware_filled_ >= ware_capacity_) return false;
+
+ DescriptionIndex const descr_index = ware.descr_index();
+ for (int i = 0; i < ware_filled_; ++i) {
+ if (wares_[i].ware->descr_index() == descr_index) return false;
+ }
+ return true;
+}
+
+/**
* Returns true if the flag can hold more wares.
*/
bool Flag::has_capacity() const {
@@ -339,10 +355,13 @@
}
void Flag::add_ware(EditorGameBase& egbase, WareInstance& ware) {
-
assert(ware_filled_ < ware_capacity_);
+ init_ware(egbase, ware, wares_[ware_filled_++]);
+ if (upcast(Game, game, &egbase))
+ ware.update(*game); // will call call_carrier() if necessary
+}
- PendingWare& pi = wares_[ware_filled_++];
+void Flag::init_ware(EditorGameBase& egbase, WareInstance& ware, PendingWare& pi) {
pi.ware = &ware;
pi.pending = false;
pi.nextstep = nullptr;
@@ -362,29 +381,22 @@
}
ware.set_location(egbase, this);
-
- if (upcast(Game, game, &egbase))
- ware.update(*game); // will call call_carrier() if necessary
}
/**
- * \return true if an ware is currently waiting for a carrier to the given Flag.
+ * \return a ware currently waiting for a carrier to the given destflag.
*
* \note Due to fetch_from_flag() semantics, this function makes no sense
* for a building destination.
*/
-bool Flag::has_pending_ware(Game&, Flag& dest) {
+Flag::PendingWare* Flag::has_pending_ware_for_flag(Flag& destflag) {
for (int32_t i = 0; i < ware_filled_; ++i) {
- if (!wares_[i].pending)
- continue;
-
- if (wares_[i].nextstep != &dest)
- continue;
-
- return true;
+ PendingWare* pw = &wares_[i];
+ if (pw->pending && pw->nextstep == &destflag &&
+ destflag.allow_ware_from_flag(*pw->ware, *this)) return pw;
}
- return false;
+ return nullptr;
}
/**
@@ -394,101 +406,112 @@
#define MAX_TRANSFER_PRIORITY 16
/**
- * Called by carrier code to indicate that the carrier is moving to pick up an
- * ware. Ware with highest transfer priority is chosen.
- * \return true if an ware is actually waiting for the carrier.
- */
-bool Flag::ack_pickup(Game&, Flag& destflag) {
- int32_t highest_pri = -1;
- int32_t i_pri = -1;
-
- for (int32_t i = 0; i < ware_filled_; ++i) {
- if (!wares_[i].pending)
- continue;
-
- if (wares_[i].nextstep != &destflag)
- continue;
-
- if (wares_[i].priority > highest_pri) {
- highest_pri = wares_[i].priority;
- i_pri = i;
-
- // Increase ware priority, it matters only if the ware has to wait.
- if (wares_[i].priority < MAX_TRANSFER_PRIORITY)
- wares_[i].priority++;
- }
- }
-
- if (i_pri >= 0) {
- wares_[i_pri].pending = false;
- return true;
- }
-
- return false;
-}
-/**
* Called by the carriercode when the carrier is called away from his job
* but has acknowledged a ware before. This ware is then freed again
* to be picked by another carrier. Returns true if an ware was indeed
* made pending again
*/
bool Flag::cancel_pickup(Game& game, Flag& destflag) {
- int32_t lowest_prio = MAX_TRANSFER_PRIORITY + 1;
- int32_t i_pri = -1;
-
for (int32_t i = 0; i < ware_filled_; ++i) {
- if (wares_[i].pending)
- continue;
-
- if (wares_[i].nextstep != &destflag)
- continue;
-
- if (wares_[i].priority < lowest_prio) {
- lowest_prio = wares_[i].priority;
- i_pri = i;
+ PendingWare& pw = wares_[i];
+ if (!pw.pending && pw.nextstep == &destflag) {
+ pw.pending = true;
+ pw.ware->update(game); // will call call_carrier() if necessary
+ return true;
}
}
- if (i_pri >= 0) {
- wares_[i_pri].pending = true;
- wares_[i_pri].ware->update(game); // will call call_carrier() if necessary
- return true;
- }
-
return false;
}
/**
- * Wake one sleeper from the capacity queue.
-*/
-void Flag::wake_up_capacity_queue(Game& game) {
- while (!capacity_wait_.empty()) {
- Worker* const w = capacity_wait_[0].get(game);
- capacity_wait_.erase(capacity_wait_.begin());
- if (w && w->wakeup_flag_capacity(game, *this))
- break;
- }
-}
-
-/**
- * Called by carrier code to retrieve one of the wares on the flag that is meant
- * for that carrier.
- *
- * This function may return 0 even if \ref ack_pickup() has already been
- * called successfully.
-*/
-WareInstance* Flag::fetch_pending_ware(Game& game, PlayerImmovable& dest) {
- int32_t best_index = -1;
-
- for (int32_t i = 0; i < ware_filled_; ++i) {
- if (wares_[i].nextstep != &dest)
- continue;
-
- // We prefer to retrieve wares that have already been acked
- if (best_index < 0 || !wares_[i].pending)
- best_index = i;
- }
-
+ * Called by carrier code to find the best among the wares on this flag
+ * that are meant for the provided dest.
+ * \return index of found ware or -1 if not found appropriate
+*/
+int32_t Flag::find_pending_ware(PlayerImmovable& dest) {
+ int32_t highest_pri = -1;
+ int32_t best_index = -1;
+ bool ware_pended = false;
+
+ for (int32_t i = 0; i < ware_filled_; ++i) {
+ PendingWare& pw = wares_[i];
+ if (pw.nextstep != &dest) continue;
+
+ if (pw.priority < MAX_TRANSFER_PRIORITY) pw.priority++;
+ // Release promised pickup, in case we find a preferable ware
+ if (!ware_pended && !pw.pending) {
+ ware_pended = pw.pending = true;
+ }
+
+ // If dest is flag, we exclude wares that can stress it
+ if (&dest != building_ &&
+ !dynamic_cast<Flag&>(dest).allow_ware_from_flag(*pw.ware, *this)) {
+ continue;
+ }
+
+ if (pw.priority > highest_pri) {
+ highest_pri = pw.priority;
+ best_index = i;
+ }
+ }
+
+ return best_index;
+}
+
+/**
+ * Like find_pending_ware above, but for carriers who are currently carrying a ware.
+ * \return -2 if denied drop
+*/
+int32_t Flag::find_swapable_ware(WareInstance& ware, Flag& destflag) {
+ DescriptionIndex const descr_index = ware.descr_index();
+ int32_t highest_pri = -1;
+ int32_t best_index = -1;
+ bool has_same_ware = false;
+ bool has_allowed = false;
+ bool ware_pended = false;
+
+ for (int32_t i = 0; i < ware_filled_; ++i) {
+ PendingWare& pw = wares_[i];
+ if (pw.nextstep != &destflag) {
+ if (pw.ware->descr_index() == descr_index) has_same_ware = true;
+ continue;
+ }
+
+ if (pw.priority < MAX_TRANSFER_PRIORITY) pw.priority++;
+ // Release promised pickup, in case we find a preferable ware
+ if (!ware_pended && !pw.pending) {
+ ware_pended = pw.pending = true;
+ }
+
+ // We prefer to retrieve wares that won't stress the destflag
+ if (destflag.allow_ware_from_flag(*pw.ware, *this)) {
+ if (!has_allowed) {
+ has_allowed = true;
+ highest_pri = -1;
+ }
+ } else {
+ if (has_allowed) continue;
+ }
+
+ if (pw.priority > highest_pri) {
+ highest_pri = pw.priority;
+ best_index = i;
+ }
+ }
+
+ if (best_index > -1) {
+ return (ware_filled_ > ware_capacity_ - 3 || has_allowed) ? best_index : -1;
+ } else {
+ return (ware_filled_ < ware_capacity_ - 2 ||
+ (ware_filled_ < ware_capacity_ && !has_same_ware)) ? -1 : -2;
+ }
+}
+
+/**
+ * Called by carrier code to retrieve a ware found by the previous methods.
+*/
+WareInstance* Flag::fetch_pending_ware(Game& game, int32_t best_index) {
if (best_index < 0)
return nullptr;
@@ -499,14 +522,39 @@
sizeof(wares_[0]) * (ware_filled_ - best_index));
ware->set_location(game, nullptr);
-
- // wake up capacity wait queue
- wake_up_capacity_queue(game);
-
return ware;
}
/**
+ * Called by carrier code to notify waiting carriers
+ * which may be interested in the new state of this flag.
+*/
+void Flag::ware_departing(Game& game) {
+ // Wake up one sleeper from the capacity queue.
+ while (!capacity_wait_.empty()) {
+ Worker* const w = capacity_wait_[0].get(game);
+ capacity_wait_.erase(capacity_wait_.begin());
+ if (w && w->wakeup_flag_capacity(game, *this)) return;
+ }
+
+ // Consider pending wares of neighboring flags.
+ for (int32_t dir = 1; dir <= 6; ++dir) {
+ Road* const road = get_road(dir);
+ if (!road) continue;
+
+ Flag* other = &road->get_flag(Road::FlagEnd);
+ if (other == this) {
+ other = &road->get_flag(Road::FlagStart);
+ }
+
+ PendingWare* pw = other->has_pending_ware_for_flag(*this);
+ if (pw && road->notify_ware(game, *other)) {
+ pw->pending = false;
+ }
+ }
+}
+
+/**
* Return a List of all the wares currently on this Flag. Do not rely
* the result value to stay valid and do not change them
*/
@@ -532,7 +580,7 @@
memmove(&wares_[i], &wares_[i + 1], sizeof(wares_[0]) * (ware_filled_ - i));
if (upcast(Game, game, &egbase))
- wake_up_capacity_queue(*game);
+ ware_departing(*game);
return;
}
@@ -600,27 +648,16 @@
for (int32_t dir = 1; dir <= 6; ++dir) {
Road* const road = get_road(dir);
- Flag* other;
- Road::FlagId flagid;
-
- if (!road) {
- continue;
- }
-
- if (&road->get_flag(Road::FlagStart) == this) {
- flagid = Road::FlagStart;
- other = &road->get_flag(Road::FlagEnd);
- } else {
- flagid = Road::FlagEnd;
+ if (!road) continue;
+
+ Flag* other = &road->get_flag(Road::FlagEnd);
+ if (other == this) {
other = &road->get_flag(Road::FlagStart);
}
-
- if (other != &nextflag) {
- continue;
- }
+ if (other != &nextflag) continue;
// Yes, this is the road we want; inform it
- if (road->notify_ware(game, flagid)) {
+ if (other->update_ware_from_flag(game, wares_[i], *road, *this)) {
return;
}
@@ -634,6 +671,70 @@
}
/**
+ * Called by neighboring flags, before agreeing for a carrier
+ * to take one of their wares heading to this flag.
+ * \return true/allow on low congestion-risk.
+*/
+bool Flag::allow_ware_from_flag(WareInstance& ware, Flag& flag) {
+ // avoid iteration for the easy cases
+ if (ware_filled_ < ware_capacity_ - 2) return true;
+
+ DescriptionIndex const descr_index = ware.descr_index();
+ bool has_swapable = false;
+ for (int i = 0; i < ware_filled_; ++i) {
+ PendingWare& pw = wares_[i];
+ if (pw.pending && pw.nextstep == &flag) {
+ has_swapable = true;
+ } else if (pw.ware->descr_index() == descr_index) {
+ return false;
+ }
+ }
+ return (ware_filled_ < ware_capacity_ || has_swapable) ? true : false;
+}
+
+/**
+ * Called when a ware is trying to reach this flag through the provided road,
+ * having just arrived to the provided flag.
+ * Swaps pending wares if possible. Otherwise,
+ * asks road for carrier on low congestion-risk.
+ * \return false if the ware is not immediately served.
+*/
+bool Flag::update_ware_from_flag(Game& game, PendingWare& pw1, Road& road, Flag& flag) {
+ WareInstance& w1 = *pw1.ware;
+ DescriptionIndex const w1_descr_index = w1.descr_index();
+ bool has_same_ware = false;
+ bool has_swapable = false;
+ for (int i = 0; i < ware_filled_; ++i) {
+ PendingWare& pw2 = wares_[i];
+ WareInstance& w2 = *pw2.ware;
+ if (w2.descr_index() == w1_descr_index) {
+ if (pw2.nextstep == &flag) {
+ // swap pending wares remotely
+ init_ware(game, w1, pw2);
+ flag.init_ware(game, w2, pw1);
+ w1.update(game);
+ w2.update(game);
+ return true;
+ }
+
+ has_same_ware = true;
+ } else if (pw2.pending && pw2.nextstep == &flag) {
+ has_swapable = true;
+ }
+ }
+
+ // ask road for carrier on low congestion-risk
+ if (ware_filled_ < ware_capacity_ - 2 ||
+ (!has_same_ware && (ware_filled_ < ware_capacity_ || has_swapable))) {
+ if (road.notify_ware(game, flag)) {
+ pw1.pending = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
* Called whenever a road gets broken or split.
* Make sure all wares on this flag are rerouted if necessary.
*
=== modified file 'src/economy/flag.h'
--- src/economy/flag.h 2018-04-07 16:59:00 +0000
+++ src/economy/flag.h 2018-06-17 23:13:32 +0000
@@ -116,7 +116,15 @@
bool is_dead_end() const;
+ struct PendingWare {
+ WareInstance* ware; ///< the ware itself
+ bool pending; ///< if the ware is pending
+ int32_t priority; ///< carrier prefers the ware with highest priority
+ OPtr<PlayerImmovable> nextstep; ///< next step that this ware is sent to
+ };
+
bool has_capacity() const;
+ bool has_capacity_for_ware(WareInstance&) const;
uint32_t total_capacity() {
return ware_capacity_;
}
@@ -126,10 +134,14 @@
void wait_for_capacity(Game&, Worker&);
void skip_wait_for_capacity(Game&, Worker&);
void add_ware(EditorGameBase&, WareInstance&);
- bool has_pending_ware(Game&, Flag& destflag);
- bool ack_pickup(Game&, Flag& destflag);
+ void init_ware(EditorGameBase&, WareInstance&, PendingWare&);
+ PendingWare* has_pending_ware_for_flag(Flag&);
bool cancel_pickup(Game&, Flag& destflag);
- WareInstance* fetch_pending_ware(Game&, PlayerImmovable& dest);
+ void ware_departing(Game&);
+ bool allow_ware_from_flag(WareInstance&, Flag&);
+ int32_t find_swapable_ware(WareInstance&, Flag&);
+ int32_t find_pending_ware(PlayerImmovable&);
+ WareInstance* fetch_pending_ware(Game&, int32_t);
Wares get_wares();
void call_carrier(Game&, WareInstance&, PlayerImmovable* nextstep);
@@ -151,20 +163,13 @@
float scale,
RenderTarget* dst) override;
- void wake_up_capacity_queue(Game&);
-
static void
flag_job_request_callback(Game&, Request&, DescriptionIndex, Worker*, PlayerImmovable&);
void set_flag_position(Coords coords);
private:
- struct PendingWare {
- WareInstance* ware; ///< the ware itself
- bool pending; ///< if the ware is pending
- int32_t priority; ///< carrier prefers the ware with highest priority
- OPtr<PlayerImmovable> nextstep; ///< next step that this ware is sent to
- };
+ bool update_ware_from_flag(Game&, PendingWare&, Road&, Flag&);
struct FlagJob {
Request* request;
=== modified file 'src/economy/road.cc'
--- src/economy/road.cc 2018-04-07 16:59:00 +0000
+++ src/economy/road.cc 2018-06-17 23:13:32 +0000
@@ -542,7 +542,8 @@
* Called by Flag code: an ware should be picked up from the given flag.
* \return true if a carrier has been sent on its way, false otherwise.
*/
-bool Road::notify_ware(Game& game, FlagId const flagid) {
+bool Road::notify_ware(Game& game, Flag& flag) {
+ FlagId flagid = &flag == flags_[Road::FlagEnd] ? Road::FlagEnd : Road::FlagStart;
uint32_t const gametime = game.get_gametime();
assert(busyness_last_update_ <= gametime);
uint32_t const tdelta = gametime - busyness_last_update_;
=== modified file 'src/economy/road.h'
--- src/economy/road.h 2018-04-07 16:59:00 +0000
+++ src/economy/road.h 2018-06-17 23:13:32 +0000
@@ -107,7 +107,7 @@
void presplit(Game&, Coords split);
void postsplit(Game&, Flag&);
- bool notify_ware(Game& game, FlagId flagid);
+ bool notify_ware(Game& game, Flag& flag);
void remove_worker(Worker&) override;
void assign_carrier(Carrier&, uint8_t);
=== modified file 'src/logic/map_objects/tribes/carrier.cc'
--- src/logic/map_objects/tribes/carrier.cc 2018-04-15 06:13:09 +0000
+++ src/logic/map_objects/tribes/carrier.cc 2018-06-17 23:13:32 +0000
@@ -43,6 +43,7 @@
static_cast<Bob::Ptr>(&Carrier::road_pop), true};
/**
+ * Called by road code when the carrier has arrived successfully.
* Work on the given road, assume the location is correct.
*/
void Carrier::start_task_road(Game& game) {
@@ -50,7 +51,7 @@
top_state().ivar1 = 0;
- promised_pickup_to_ = NOONE;
+ operation_ = INIT;
}
/**
@@ -77,14 +78,14 @@
return pop_task(game);
}
- // Check for pending wares
- if (promised_pickup_to_ == NOONE)
- find_pending_ware(game);
+ if (operation_ == INIT) {
+ operation_ = find_source_flag(game);
+ }
- if (promised_pickup_to_ != NOONE) {
+ if (operation_ > NOP) {
if (state.ivar1) {
state.ivar1 = 0;
- return start_task_transport(game, promised_pickup_to_);
+ return start_task_transport(game, operation_);
} else {
// Short delay before we move to pick up
state.ivar1 = 1;
@@ -114,10 +115,10 @@
* a ware there, we have to make sure that they do not count on us anymore.
*/
void Carrier::road_pop(Game& game, State& /* state */) {
- if (promised_pickup_to_ != NOONE && get_location(game)) {
+ if (operation_ > NOP && get_location(game)) {
Road& road = dynamic_cast<Road&>(*get_location(game));
- Flag& flag = road.get_flag(static_cast<Road::FlagId>(promised_pickup_to_));
- Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(promised_pickup_to_ ^ 1));
+ Flag& flag = road.get_flag(static_cast<Road::FlagId>(operation_));
+ Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(operation_ ^ 1));
flag.cancel_pickup(game, otherflag);
}
@@ -155,35 +156,85 @@
return pop_task(game);
}
- if (state.ivar1 == -1)
+ int32_t const ivar1 = state.ivar1;
+ if (ivar1 == -1)
// If we're "in" the target building, special code applies
- deliver_to_building(game, state);
-
- else if (!does_carry_ware())
- // If we don't carry something, walk to the flag
- pickup_from_flag(game, state);
-
- else {
- Road& road = dynamic_cast<Road&>(*get_location(game));
- // If the ware should go to the building attached to our flag, walk
- // directly into said building
- Flag& flag = road.get_flag(static_cast<Road::FlagId>(state.ivar1 ^ 1));
-
- WareInstance& ware = *get_carried_ware(game);
- assert(ware.get_location(game) == this);
-
+ return deliver_to_building(game, state);
+
+ WareInstance* ware = get_carried_ware(game);
+ if (ware) {
+ assert(ware->get_location(game) == this);
+ }
+
+ Road& road = dynamic_cast<Road&>(*get_location(game));
+ int32_t const dest = ware ? ivar1 ^ 1 : ivar1;
+ Flag& flag = road.get_flag(static_cast<Road::FlagId>(dest));
+
+ if (ware) {
+ // If the ware should go to the building attached to our flag,
+ // walk directly into said building
// A sanity check is necessary, in case the building has been destroyed
- PlayerImmovable* const next = ware.get_next_move_step(game);
-
- if (next && next != &flag && &next->base_flag() == &flag)
- enter_building(game, state);
-
- // If the flag is overloaded we are allowed to drop wares as
- // long as we can pick another up. Otherwise we have to wait.
- else if ((flag.has_capacity() || !swap_or_wait(game, state)) &&
- !start_task_walktoflag(game, state.ivar1 ^ 1))
- // Drop the ware, possible exchanging it with another one
- drop_ware(game, state);
+
+ PlayerImmovable* next = ware->get_next_move_step(game);
+ if (next && next != &flag && &next->base_flag() == &flag) {
+ if (!start_task_walktoflag(game, dest)) {
+ // Enter building
+ state.ivar1 = -1;
+ start_task_move(game, WALK_NW, descr().get_right_walk_anims(does_carry_ware()), true);
+ }
+ return;
+ }
+ }
+
+ if (!start_task_walktoflag(game, dest, operation_ == WAIT)) {
+ // If the flag is overloaded we are allowed to drop wares,
+ // as long as we can pick another up. Otherwise we have to wait.
+
+ Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(dest ^ 1));
+ int32_t otherware_idx = ware ? flag.find_swapable_ware(*ware, otherflag) :
+ flag.find_pending_ware(otherflag);
+ if (operation_ == WAIT) {
+ if (otherware_idx < -1) {
+ return start_task_waitforcapacity(game, flag); // join flag's wait queue
+ } else {
+ operation_ = dest ^ 1; // resume transport without joining flag's wait queue
+ set_animation(game, descr().get_animation("idle"));
+ return schedule_act(game, 20);
+ }
+ } else if (otherware_idx < -1) {
+ operation_ = WAIT; // move one node away
+ set_animation(game, descr().get_animation("idle"));
+ return schedule_act(game, 20);
+ }
+
+ WareInstance* otherware = flag.fetch_pending_ware(game, otherware_idx);
+
+ if (ware) {
+ // Drop our ware
+ flag.add_ware(game, *fetch_carried_ware(game));
+ }
+
+ // Pick up new load, if any
+ if (otherware) {
+ set_carried_ware(game, otherware);
+ flag.ware_departing(game);
+
+ operation_ = state.ivar1 = dest;
+ set_animation(game, descr().get_animation("idle"));
+ schedule_act(game, 20);
+ } else {
+ Flag::PendingWare* pw = otherflag.has_pending_ware_for_flag(flag);
+ if (pw) {
+ pw->pending = false;
+
+ operation_ = state.ivar1 = dest ^ 1;
+ set_animation(game, descr().get_animation("idle"));
+ schedule_act(game, 20);
+ } else {
+ operation_ = NOP;
+ pop_task(game);
+ }
+ }
}
}
@@ -197,9 +248,10 @@
void Carrier::deliver_to_building(Game& game, State& state) {
BaseImmovable* const pos = game.map()[get_position()].get_immovable();
- if (dynamic_cast<Flag const*>(pos))
+ if (dynamic_cast<Flag const*>(pos)) {
+ operation_ = INIT;
return pop_task(game); // we are done
- else if (upcast(Building, building, pos)) {
+ } else if (upcast(Building, building, pos)) {
// Drop all wares addressed to this building
while (WareInstance* const ware = get_carried_ware(game)) {
// If the building has disappeared and immediately been replaced
@@ -230,201 +282,51 @@
}
/**
- * Walks to the queued flag and picks up one acked ware
- *
- * \param g Game the carrier lives on
- * \param s Flags sent to the task
- */
-void Carrier::pickup_from_flag(Game& game, State& state) {
- int32_t const ivar1 = state.ivar1;
- if (!start_task_walktoflag(game, ivar1)) {
-
- promised_pickup_to_ = NOONE;
-
- Road& road = dynamic_cast<Road&>(*get_location(game));
- Flag& flag = road.get_flag(static_cast<Road::FlagId>(ivar1));
- Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(ivar1 ^ 1));
-
- // Are there wares to move between our flags?
- if (WareInstance* const ware = flag.fetch_pending_ware(game, otherflag)) {
- set_carried_ware(game, ware);
-
- set_animation(game, descr().get_animation("idle"));
- return schedule_act(game, 20);
- } else {
- molog("[Carrier]: Nothing suitable on flag.\n");
- return pop_task(game);
- }
- }
-}
-
-/**
- * Drop one ware in a flag, and pick up a new one if we acked it
- *
- * \param g Game the carrier lives on.
- * \param s Flags sent to the task
- */
-void Carrier::drop_ware(Game& game, State& state) {
- WareInstance* other = nullptr;
- Road& road = dynamic_cast<Road&>(*get_location(game));
- Flag& flag = road.get_flag(static_cast<Road::FlagId>(state.ivar1 ^ 1));
-
- if (promised_pickup_to_ == (state.ivar1 ^ 1)) {
- // If there's an ware we acked, we can drop ours even if the flag is
- // flooded
- other = flag.fetch_pending_ware(game, road.get_flag(static_cast<Road::FlagId>(state.ivar1)));
-
- if (!other && !flag.has_capacity()) {
- molog("[Carrier]: strange: acked ware from busy flag no longer "
- "present.\n");
-
- promised_pickup_to_ = NOONE;
- set_animation(game, descr().get_animation("idle"));
- return schedule_act(game, 20);
- }
-
- state.ivar1 = promised_pickup_to_;
- promised_pickup_to_ = NOONE;
- }
-
- // Drop our ware
- flag.add_ware(game, *fetch_carried_ware(game));
-
- // Pick up new load, if any
- if (other) {
- set_carried_ware(game, other);
-
- set_animation(game, descr().get_animation("idle"));
- return schedule_act(game, 20);
- } else
- return pop_task(game);
-}
-
-/**
- * When picking up wares, if some of them is targeted to the building attached
- * to target flag walk straight into it and deliver.
- *
- * \param g Game the carrier lives on.
- * \param s Flags sent to the task.
- */
-void Carrier::enter_building(Game& game, State& state) {
- if (!start_task_walktoflag(game, state.ivar1 ^ 1)) {
- state.ivar1 = -1;
- return start_task_move(game, WALK_NW, descr().get_right_walk_anims(does_carry_ware()), true);
- }
-}
-
-/**
- * Swaps wares from an overloaded flag for as long as the carrier can pick
- * up new wares from it. Otherwise, changes the carrier state to wait.
- *
- * \param g Game the carrier lives on.
- * \param s Flags sent to the task.
- *
- * \return true if the carrier must wait before delivering his wares.
- */
-bool Carrier::swap_or_wait(Game& game, State& state) {
- // Road that employs us
- Road& road = dynamic_cast<Road&>(*get_location(game));
- // Flag we are delivering to
- Flag& flag = road.get_flag(static_cast<Road::FlagId>(state.ivar1 ^ 1));
- // The other flag of our road
- Flag& otherflag = road.get_flag(static_cast<Road::FlagId>(state.ivar1));
-
- if (promised_pickup_to_ == (state.ivar1 ^ 1)) {
- // All is well, we already acked an ware that we can pick up
- // from this flag
- return false;
- } else if (flag.has_pending_ware(game, otherflag)) {
- if (!flag.ack_pickup(game, otherflag))
- throw wexception(
- "MO(%u): transport: overload exchange: flag %u is fucked up", serial(), flag.serial());
-
- promised_pickup_to_ = state.ivar1 ^ 1;
- return false;
- } else if (!start_task_walktoflag(game, state.ivar1 ^ 1, true))
- start_task_waitforcapacity(game, flag); // wait one node away
-
- return true;
-}
-
-/**
- * Called by Road code to indicate that a new ware has arrived on a flag
- * (0 = start, 1 = end).
+ * Called by road code to indicate that the given flag
+ * (0 = start, 1 = end) has a ware ready for transfer.
* \return true if the carrier is going to fetch it.
*/
bool Carrier::notify_ware(Game& game, int32_t const flag) {
State& state = top_state();
- // Check if we've already acked something
- if (promised_pickup_to_ != NOONE)
- return false;
-
- // If we are currently in a transport.
- // Explanation:
- // a) a different carrier / road may be better suited for this ware
- // (the transport code does not have priorities for the actual
- // carrier that is notified)
- // b) the transport task has logic that allows it to
- // drop an ware on an overloaded flag iff it can pick up an ware
- // at the same time.
- // We should ack said ware to avoid more confusion before we move
- // onto the flag, but we can't do that if we have already acked
- // something.
- // c) we might ack for a flag that we are actually moving away from;
- // this will get us into trouble if wares have arrived on the other
- // flag while we couldn't ack them.
- //
- // (Maybe the need for this lengthy explanation is proof that the
- // ack system needs to be reworked.)
- if (State const* const transport = get_state(taskTransport))
- if ((transport->ivar1 == -1 && find_closest_flag(game) != flag) || flag == transport->ivar1)
- return false;
-
- // Ack it if we haven't
- promised_pickup_to_ = flag;
-
- if (state.task == &taskRoad)
+ if (operation_ == WAIT) {
+ if (state.objvar1.get(game) ==
+ &dynamic_cast<Road&>(*get_location(game)).get_flag(static_cast<Road::FlagId>(flag))) {
+ operation_ = flag;
+ send_signal(game, "wakeup");
+ return true;
+ }
+ } else if (operation_ == NOP) {
+ operation_ = flag;
send_signal(game, "ware");
- else if (state.task == &taskWaitforcapacity)
- send_signal(game, "wakeup");
+ return true;
+ }
- return true;
+ return false;
}
/**
- * Find a pending ware on one of the road's flags, ack it and set promised_pickup_to_
- * accordingly.
+ * Find a pending ware meant for our road,
+ * remove its pending status, and
+ * \return the flag it is on.
*/
-void Carrier::find_pending_ware(Game& game) {
+int32_t Carrier::find_source_flag(Game& game) {
+ assert(operation_ == INIT);
+
Road& road = dynamic_cast<Road&>(*get_location(game));
- uint32_t havewarebits = 0;
-
- assert(promised_pickup_to_ == NOONE);
-
- if (road.get_flag(Road::FlagStart).has_pending_ware(game, road.get_flag(Road::FlagEnd))) {
- havewarebits |= 1;
- }
-
- if (road.get_flag(Road::FlagEnd).has_pending_ware(game, road.get_flag(Road::FlagStart))) {
- havewarebits |= 2;
- }
-
- // If both flags have an ware, we pick the one closer to us.
- if (havewarebits == 3) {
- havewarebits = 1 << find_closest_flag(game);
- }
-
- // Ack our decision
- if (havewarebits == 1) {
- promised_pickup_to_ = START_FLAG;
- if (!road.get_flag(Road::FlagStart).ack_pickup(game, road.get_flag(Road::FlagEnd)))
- throw wexception("Carrier::find_pending_ware: start flag is messed up");
-
- } else if (havewarebits == 2) {
- promised_pickup_to_ = END_FLAG;
- if (!road.get_flag(Road::FlagEnd).ack_pickup(game, road.get_flag(Road::FlagStart)))
- throw wexception("Carrier::find_pending_ware: end flag is messed up");
+ int32_t near = find_closest_flag(game);
+ Flag& nearflag = road.get_flag(static_cast<Road::FlagId>(near));
+ Flag& farflag = road.get_flag(static_cast<Road::FlagId>(near ^ 1));
+
+ Flag::PendingWare* pw;
+ if (pw = nearflag.has_pending_ware_for_flag(farflag)) {
+ pw->pending = false;
+ return near;
+ } else if (pw = farflag.has_pending_ware_for_flag(nearflag)) {
+ pw->pending = false;
+ return near ^ 1;
+ } else {
+ return NOP;
}
}
@@ -497,7 +399,7 @@
Worker::log_general_info(egbase);
- molog("promised_pickup_to = %i\n", promised_pickup_to_);
+ molog("operation_ = %i\n", operation_);
}
/*
@@ -521,7 +423,7 @@
uint8_t packet_version = fr.unsigned_8();
if (packet_version == kCurrentPacketVersion) {
Carrier& carrier = get<Carrier>();
- carrier.promised_pickup_to_ = fr.signed_32();
+ carrier.operation_ = fr.signed_32();
} else {
throw UnhandledVersionError("Carrier", packet_version, kCurrentPacketVersion);
}
@@ -546,7 +448,7 @@
Worker::do_save(egbase, mos, fw);
fw.unsigned_8(kCurrentPacketVersion);
- fw.signed_32(promised_pickup_to_);
+ fw.signed_32(operation_);
}
CarrierDescr::CarrierDescr(const std::string& init_descname,
=== modified file 'src/logic/map_objects/tribes/carrier.h'
--- src/logic/map_objects/tribes/carrier.h 2018-04-15 06:13:09 +0000
+++ src/logic/map_objects/tribes/carrier.h 2018-06-17 23:13:32 +0000
@@ -24,6 +24,7 @@
#include "logic/map_objects/tribes/worker.h"
namespace Widelands {
+class PendingWare;
class CarrierDescr : public WorkerDescr {
public:
@@ -49,7 +50,7 @@
MO_DESCR(CarrierDescr)
explicit Carrier(const CarrierDescr& carrier_descr)
- : Worker(carrier_descr), promised_pickup_to_(NOONE) {
+ : Worker(carrier_descr), operation_(NOP) {
}
~Carrier() override {
}
@@ -66,7 +67,7 @@
static Task const taskRoad;
private:
- void find_pending_ware(Game&);
+ int32_t find_source_flag(Game&);
int32_t find_closest_flag(Game&);
// internal task stuff
@@ -77,17 +78,14 @@
static Task const taskTransport;
void deliver_to_building(Game&, State&);
- void pickup_from_flag(Game&, State&);
- void drop_ware(Game&, State&);
- void enter_building(Game&, State&);
- bool swap_or_wait(Game&, State&);
- /// -1: no ware acked; 0/1: acked ware for start/end flag of road
// This should be an enum, but this clutters the code with too many casts
- static const int32_t NOONE = -1;
- static const int32_t START_FLAG = 0;
- static const int32_t END_FLAG = 1;
- int32_t promised_pickup_to_;
+ static const int32_t INIT = -3; // ready to undertake or resume operations
+ static const int32_t WAIT = -2; // waiting for flag capacity
+ static const int32_t NOP = -1; // idling
+ static const int32_t START_FLAG = 0; // serving start flag of road
+ static const int32_t END_FLAG = 1; // serving end flag of road
+ int32_t operation_;
// saving and loading
protected:
=== modified file 'src/logic/map_objects/tribes/worker.cc'
--- src/logic/map_objects/tribes/worker.cc 2018-05-05 17:10:37 +0000
+++ src/logic/map_objects/tribes/worker.cc 2018-06-17 23:13:32 +0000
@@ -1840,12 +1840,11 @@
if (upcast(Flag, flag, pos)) {
// Is this "our" flag?
if (flag->get_building() == location) {
- if (state.ivar1 && flag->has_capacity()) {
- if (WareInstance* const ware = fetch_carried_ware(game)) {
- flag->add_ware(game, *ware);
- set_animation(game, descr().get_animation("idle"));
- return schedule_act(game, 20); // rest a while
- }
+ WareInstance* const ware = get_carried_ware(game);
+ if (state.ivar1 && ware && flag->has_capacity_for_ware(*ware)) {
+ flag->add_ware(game, *fetch_carried_ware(game));
+ set_animation(game, descr().get_animation("idle"));
+ return schedule_act(game, 20); // rest a while
}
// Don't try to enter building if it is a dismantle site
@@ -2078,15 +2077,18 @@
if (ware) {
// We're in the building, walk onto the flag
if (upcast(Building, building, location)) {
- if (start_task_waitforcapacity(game, building->base_flag()))
- return;
-
- return start_task_leavebuilding(game, false); // exit throttle
+ Flag& baseflag = building->base_flag();
+ if (baseflag.has_capacity_for_ware(*ware)) {
+ start_task_leavebuilding(game, false); // exit throttle
+ } else {
+ start_task_waitforcapacity(game, baseflag);
+ }
+ return;
}
// We're on the flag, drop the ware and pause a little
if (upcast(Flag, flag, location)) {
- if (flag->has_capacity()) {
+ if (flag->has_capacity_for_ware(*ware)) {
flag->add_ware(game, *fetch_carried_ware(game));
set_animation(game, descr().get_animation("idle"));
@@ -2166,9 +2168,10 @@
// The ware has decided that it doesn't want to go to us after all
// In order to return to the warehouse, we're switching to State_DropOff
- if (WareInstance* const ware =
- dynamic_cast<Flag&>(*location).fetch_pending_ware(game, employer)) {
+ Flag& flag = dynamic_cast<Flag&>(*location);
+ if (WareInstance* const ware = flag.fetch_pending_ware(game, flag.find_pending_ware(employer))) {
set_carried_ware(game, ware);
+ flag.ware_departing(game);
}
set_animation(game, descr().get_animation("idle"));
@@ -2231,24 +2234,15 @@
static_cast<Bob::Ptr>(&Worker::waitforcapacity_pop), true};
/**
- * Checks the capacity of the flag.
- *
- * If there is none, a wait task is pushed, and the worker is added to the
- * flag's wait queue. The function returns true in this case.
- * If the flag still has capacity, the function returns false and doesn't
- * act at all.
+ * Pushes a wait task and
+ * adds the worker to the flag's wait queue.
*/
-bool Worker::start_task_waitforcapacity(Game& game, Flag& flag) {
- if (flag.has_capacity())
- return false;
-
+void Worker::start_task_waitforcapacity(Game& game, Flag& flag) {
push_task(game, taskWaitforcapacity);
top_state().objvar1 = &flag;
flag.wait_for_capacity(game, *this);
-
- return true;
}
void Worker::waitforcapacity_update(Game& game, State&) {
=== modified file 'src/logic/map_objects/tribes/worker.h'
--- src/logic/map_objects/tribes/worker.h 2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/tribes/worker.h 2018-06-17 23:13:32 +0000
@@ -163,7 +163,7 @@
void start_task_releaserecruit(Game&, Worker&);
void start_task_fetchfromflag(Game&);
- bool start_task_waitforcapacity(Game&, Flag&);
+ void start_task_waitforcapacity(Game&, Flag&);
void start_task_leavebuilding(Game&, bool changelocation);
void start_task_fugitive(Game&);
Follow ups