Merge lp:~widelands-dev/widelands/refactor_gameclient into lp:widelands

Proposed by Klaus Halfmann
Status: Merged
Merged at revision: 9125
Proposed branch: lp:~widelands-dev/widelands/refactor_gameclient
Merge into: lp:widelands
Diff against target: 1614 lines (+628/-500)
17 files modified
src/ai/defaultai.cc (+1/-1)
src/logic/game.cc (+25/-26)
src/logic/game.h (+1/-1)
src/logic/game_controller.h (+3/-1)
src/logic/replay_game_controller.cc (+1/-1)
src/logic/replay_game_controller.h (+1/-1)
src/logic/single_player_game_controller.cc (+3/-3)
src/logic/single_player_game_controller.h (+1/-1)
src/network/gameclient.cc (+548/-442)
src/network/gameclient.h (+21/-5)
src/network/gamehost.cc (+8/-8)
src/network/gamehost.h (+1/-1)
src/network/netclient_interface.h (+5/-0)
src/network/nethostproxy.cc (+1/-1)
src/wui/economy_options_window.cc (+4/-4)
src/wui/game_message_menu.cc (+3/-3)
src/wui/warehousewindow.cc (+1/-1)
To merge this branch: bzr merge lp:~widelands-dev/widelands/refactor_gameclient
Reviewer Review Type Date Requested Status
GunChleoc Approve
Klaus Halfmann Needs Resubmitting
Review via email: mp+366743@code.launchpad.net

Commit message

Refactor gameclient.h/.cc for better readability

Description of the change

* Refactor big switch statement in gameclient.cc
* Refactor ::run() to improve readability
* use send_player_command with Ptr instead of Reference to clarify ownership of PlayerCommand.
* Added more comments

Added some TODOs and empty comments where I did not know the details.

There should be no functional change at all.

To post a comment you must log in.
Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Regressiontest are ok: Ran 44 tests in 1377.106s
Will try to do a network game, too.

Revision history for this message
GunChleoc (gunchleoc) :
Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Hello Gun:
* Adressed some comments
* Get the tribe and check whether it exists before you push it
  -> I have no idea (yet) what this code does :-)
* Playercommand is not for current player , not sure if this can normally happen
  lets wait If I ever see this, then we can make it an assert.
* The This / this confusion is because I moved some fucntion into Impl, but they need
  the main class. I could use "outer" or some other name to avoid it.
* send_player_command(Widelands::PlayerCommand* pc) making this a unique_ptr:
  * Will affect a lot of code
  * Some aspects of this code are not clear to me (e.g. Commnands a queued for networking)
  -> lets adress this in a seperate branch, I already regret I touched it :-)
* I would leave "GameClientImpl* d" as is:
  * it is used very often
  * it is used in the local scope only -> no risc to loose control
  * commonly used Pattern in Widelands
* d->settings.usernum == -2,-1, n seems to be used to define the "hello" handshake
  what about "kPreHello" and "kAfterHello" constants?

Maybe we should move the two remaining variables to GameClientImpl, too?

Please check the logic of the main switch statement, I changed some control
flow for better readbility.

I intend to do a similar refactoring with gamehost, too.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4845. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/526796650.
Appveyor build 4626. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_refactor_gameclient-4626.

Revision history for this message
GunChleoc (gunchleoc) wrote :

* Get the tribe and check whether it exists before you push it
  -> I have no idea (yet) what this code does :-)

It's for the tips in the game loading screen. We have tribe-specific lists of tips.

There is a function bool Widelands::tribe_exists(const std::string& tribename) that you can use.

* The This / this confusion is because I moved some fucntion into Impl, but they need
  the main class. I could use "outer" or some other name to avoid it.

I see now - it's just an ugly variable name, yes, please change the name to "game_client" or "parent" or some such.

* send_player_command(Widelands::PlayerCommand* pc) making this a unique_ptr:
  -> lets adress this in a seperate branch, I already regret I touched it :-)

LOL fair enough. Cans or worms are best dealt with separately.

* I would leave "GameClientImpl* d" as is:
  * it is used very often
  * it is used in the local scope only -> no risc to loose control
  * commonly used Pattern in Widelands

That's because it was written before we switched to C++11, so there was no unique_ptr available. You don't have to do it in this branch though.

* d->settings.usernum == -2,-1, n seems to be used to define the "hello" handshake
  what about "kPreHello" and "kAfterHello" constants?

I have found this comment:
  /**
   * Checks if client \ref name exists and \returns int32_t :
   * - the client number if found
   * - -1 if no client was found
   * - -2 if the host is the client (has no client number)
   */

So, kUnknownClient and kUnknownHost?

* Maybe we should move the two remaining variables to GameClientImpl, too?

Consistency is good :)

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

* Used parent instead of This.
* moved all Variables into Impl

About that -2 magic: I got lost in finding the sematics of user/player etc.
I added a TODO comment with our best guess by now.

I am at the End of my vacation (sigh) so lets get this in before it
starts lingering around for too long.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Found a first bug, still need some debugging

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Still get a heap-use-after-free related to
    std::unique_ptr<UI::ProgressWindow> loader_ui(new UI::ProgressWindow());

But this is maybe just a problem as the game crashed with disconnect(CLIENT_CRASHED, )

A have a deja vue around GameClientImpl.modal which is released twice.

Revision history for this message
GunChleoc (gunchleoc) wrote :

You could also do the game tips like this:

    const std::string tribename = get_players_tribe();
    assert(Widelands::tribe_exists(tribename));
    std::unique_ptr<GameTips> tips(new GameTips(*loader, {"general_game", "multiplayer", tribename}));

As to the ASan crash, I suspect that there is a problem with the lifetime of an object caused by the pulling out of stuff into separate functions.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4871. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/527824464.
Appveyor build 4652. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_refactor_gameclient-4652.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Using unique_ptr<GameTips> is unneeded, this is only a helper class neede to as long as some progress dialog is open, so using normal scope is ok.
(We should actually cleanup that usage, some other time)

The Problem is the owenership of UI::ProgressWindow* loader

The disconnect code try to close the last 'modal' window.
In case of the loader this is incorrect.

Not sure how to cleanup tis messs by now

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Found that I lost a '!' while refactoring.

Now a normal network game works as itendend.

I assume that disconnect/double freee has always been there,
I will create a followup bug for that one.
When debugging I triggered a racecondition, I doubt we ever will see that i the wild.

Gun: and all, please take anotheer look

review: Needs Resubmitting
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4891. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/528361519.
Appveyor build 4672. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_refactor_gameclient-4672.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Now why can we nort merge this one?

Revision history for this message
GunChleoc (gunchleoc) wrote :

Sorry, your recent changes slipped me by. Thanks for the reminder, I will do another review & testing.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have done some testing and it seems to work fine. I am not getting the double free you mentioned.

@bunnybot merge

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc 2019-05-22 11:23:14 +0000
+++ src/ai/defaultai.cc 2019-05-25 17:10:52 +0000
@@ -6612,7 +6612,7 @@
6612 const uint16_t new_target = std::max<uint16_t>(default_target * multiplier / 10, 3);6612 const uint16_t new_target = std::max<uint16_t>(default_target * multiplier / 10, 3);
6613 assert(new_target > 1);6613 assert(new_target > 1);
66146614
6615 game().send_player_command(*new Widelands::CmdSetWareTargetQuantity(6615 game().send_player_command(new Widelands::CmdSetWareTargetQuantity(
6616 gametime, player_number(), observer->economy.serial(), id, new_target));6616 gametime, player_number(), observer->economy.serial(), id, new_target));
6617 }6617 }
6618 }6618 }
66196619
=== modified file 'src/logic/game.cc'
--- src/logic/game.cc 2019-05-11 18:19:20 +0000
+++ src/logic/game.cc 2019-05-25 17:10:52 +0000
@@ -711,7 +711,7 @@
711 * It takes the appropriate action, i.e. either add to the cmd_queue or send711 * It takes the appropriate action, i.e. either add to the cmd_queue or send
712 * across the network.712 * across the network.
713 */713 */
714void Game::send_player_command(PlayerCommand& pc) {714void Game::send_player_command(PlayerCommand* pc) {
715 ctrl_->send_player_command(pc);715 ctrl_->send_player_command(pc);
716}716}
717717
@@ -734,55 +734,55 @@
734734
735// we might want to make these inlines:735// we might want to make these inlines:
736void Game::send_player_bulldoze(PlayerImmovable& pi, bool const recurse) {736void Game::send_player_bulldoze(PlayerImmovable& pi, bool const recurse) {
737 send_player_command(*new CmdBulldoze(get_gametime(), pi.owner().player_number(), pi, recurse));737 send_player_command(new CmdBulldoze(get_gametime(), pi.owner().player_number(), pi, recurse));
738}738}
739739
740void Game::send_player_dismantle(PlayerImmovable& pi) {740void Game::send_player_dismantle(PlayerImmovable& pi) {
741 send_player_command(*new CmdDismantleBuilding(get_gametime(), pi.owner().player_number(), pi));741 send_player_command(new CmdDismantleBuilding(get_gametime(), pi.owner().player_number(), pi));
742}742}
743743
744void Game::send_player_build(int32_t const pid, const Coords& coords, DescriptionIndex const id) {744void Game::send_player_build(int32_t const pid, const Coords& coords, DescriptionIndex const id) {
745 assert(tribes().building_exists(id));745 assert(tribes().building_exists(id));
746 send_player_command(*new CmdBuild(get_gametime(), pid, coords, id));746 send_player_command(new CmdBuild(get_gametime(), pid, coords, id));
747}747}
748748
749void Game::send_player_build_flag(int32_t const pid, const Coords& coords) {749void Game::send_player_build_flag(int32_t const pid, const Coords& coords) {
750 send_player_command(*new CmdBuildFlag(get_gametime(), pid, coords));750 send_player_command(new CmdBuildFlag(get_gametime(), pid, coords));
751}751}
752752
753void Game::send_player_build_road(int32_t pid, Path& path) {753void Game::send_player_build_road(int32_t pid, Path& path) {
754 send_player_command(*new CmdBuildRoad(get_gametime(), pid, path));754 send_player_command(new CmdBuildRoad(get_gametime(), pid, path));
755}755}
756756
757void Game::send_player_flagaction(Flag& flag) {757void Game::send_player_flagaction(Flag& flag) {
758 send_player_command(*new CmdFlagAction(get_gametime(), flag.owner().player_number(), flag));758 send_player_command(new CmdFlagAction(get_gametime(), flag.owner().player_number(), flag));
759}759}
760760
761void Game::send_player_start_stop_building(Building& building) {761void Game::send_player_start_stop_building(Building& building) {
762 send_player_command(762 send_player_command(
763 *new CmdStartStopBuilding(get_gametime(), building.owner().player_number(), building));763 new CmdStartStopBuilding(get_gametime(), building.owner().player_number(), building));
764}764}
765765
766void Game::send_player_militarysite_set_soldier_preference(Building& building,766void Game::send_player_militarysite_set_soldier_preference(Building& building,
767 SoldierPreference my_preference) {767 SoldierPreference my_preference) {
768 send_player_command(*new CmdMilitarySiteSetSoldierPreference(768 send_player_command(new CmdMilitarySiteSetSoldierPreference(
769 get_gametime(), building.owner().player_number(), building, my_preference));769 get_gametime(), building.owner().player_number(), building, my_preference));
770}770}
771771
772void Game::send_player_start_or_cancel_expedition(Building& building) {772void Game::send_player_start_or_cancel_expedition(Building& building) {
773 send_player_command(773 send_player_command(
774 *new CmdStartOrCancelExpedition(get_gametime(), building.owner().player_number(), building));774 new CmdStartOrCancelExpedition(get_gametime(), building.owner().player_number(), building));
775}775}
776776
777void Game::send_player_enhance_building(Building& building, DescriptionIndex const id) {777void Game::send_player_enhance_building(Building& building, DescriptionIndex const id) {
778 assert(building.owner().tribe().has_building(id));778 assert(building.owner().tribe().has_building(id));
779779
780 send_player_command(780 send_player_command(
781 *new CmdEnhanceBuilding(get_gametime(), building.owner().player_number(), building, id));781 new CmdEnhanceBuilding(get_gametime(), building.owner().player_number(), building, id));
782}782}
783783
784void Game::send_player_evict_worker(Worker& worker) {784void Game::send_player_evict_worker(Worker& worker) {
785 send_player_command(*new CmdEvictWorker(get_gametime(), worker.owner().player_number(), worker));785 send_player_command(new CmdEvictWorker(get_gametime(), worker.owner().player_number(), worker));
786}786}
787787
788void Game::send_player_set_ware_priority(PlayerImmovable& imm,788void Game::send_player_set_ware_priority(PlayerImmovable& imm,
@@ -790,14 +790,14 @@
790 DescriptionIndex const index,790 DescriptionIndex const index,
791 int32_t const prio) {791 int32_t const prio) {
792 send_player_command(792 send_player_command(
793 *new CmdSetWarePriority(get_gametime(), imm.owner().player_number(), imm, type, index, prio));793 new CmdSetWarePriority(get_gametime(), imm.owner().player_number(), imm, type, index, prio));
794}794}
795795
796void Game::send_player_set_input_max_fill(PlayerImmovable& imm,796void Game::send_player_set_input_max_fill(PlayerImmovable& imm,
797 DescriptionIndex const index,797 DescriptionIndex const index,
798 WareWorker type,798 WareWorker type,
799 uint32_t const max_fill) {799 uint32_t const max_fill) {
800 send_player_command(*new CmdSetInputMaxFill(800 send_player_command(new CmdSetInputMaxFill(
801 get_gametime(), imm.owner().player_number(), imm, index, type, max_fill));801 get_gametime(), imm.owner().player_number(), imm, index, type, max_fill));
802}802}
803803
@@ -805,50 +805,49 @@
805 TrainingAttribute attr,805 TrainingAttribute attr,
806 int32_t const val) {806 int32_t const val) {
807 send_player_command(807 send_player_command(
808 *new CmdChangeTrainingOptions(get_gametime(), ts.owner().player_number(), ts, attr, val));808 new CmdChangeTrainingOptions(get_gametime(), ts.owner().player_number(), ts, attr, val));
809}809}
810810
811void Game::send_player_drop_soldier(Building& b, int32_t const ser) {811void Game::send_player_drop_soldier(Building& b, int32_t const ser) {
812 assert(ser != -1);812 assert(ser != -1);
813 send_player_command(*new CmdDropSoldier(get_gametime(), b.owner().player_number(), b, ser));813 send_player_command(new CmdDropSoldier(get_gametime(), b.owner().player_number(), b, ser));
814}814}
815815
816void Game::send_player_change_soldier_capacity(Building& b, int32_t const val) {816void Game::send_player_change_soldier_capacity(Building& b, int32_t const val) {
817 send_player_command(817 send_player_command(
818 *new CmdChangeSoldierCapacity(get_gametime(), b.owner().player_number(), b, val));818 new CmdChangeSoldierCapacity(get_gametime(), b.owner().player_number(), b, val));
819}819}
820820
821void Game::send_player_enemyflagaction(const Flag& flag,821void Game::send_player_enemyflagaction(const Flag& flag,
822 PlayerNumber const who_attacks,822 PlayerNumber const who_attacks,
823 const std::vector<Serial>& soldiers) {823 const std::vector<Serial>& soldiers) {
824 if (1 < player(who_attacks)824 if (1 < player(who_attacks)
825 .vision(Map::get_index(flag.get_building()->get_position(), map().get_width()))) {825 .vision(Map::get_index(flag.get_building()->get_position(), map().get_width())))
826 send_player_command(*new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, soldiers));826 send_player_command(new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, soldiers));
827 }
828}827}
829828
830void Game::send_player_ship_scouting_direction(Ship& ship, WalkingDir direction) {829void Game::send_player_ship_scouting_direction(Ship& ship, WalkingDir direction) {
831 send_player_command(*new CmdShipScoutDirection(830 send_player_command(new CmdShipScoutDirection(
832 get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction));831 get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction));
833}832}
834833
835void Game::send_player_ship_construct_port(Ship& ship, Coords coords) {834void Game::send_player_ship_construct_port(Ship& ship, Coords coords) {
836 send_player_command(*new CmdShipConstructPort(835 send_player_command(new CmdShipConstructPort(
837 get_gametime(), ship.get_owner()->player_number(), ship.serial(), coords));836 get_gametime(), ship.get_owner()->player_number(), ship.serial(), coords));
838}837}
839838
840void Game::send_player_ship_explore_island(Ship& ship, IslandExploreDirection direction) {839void Game::send_player_ship_explore_island(Ship& ship, IslandExploreDirection direction) {
841 send_player_command(*new CmdShipExploreIsland(840 send_player_command(new CmdShipExploreIsland(
842 get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction));841 get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction));
843}842}
844843
845void Game::send_player_sink_ship(Ship& ship) {844void Game::send_player_sink_ship(Ship& ship) {
846 send_player_command(845 send_player_command(
847 *new CmdShipSink(get_gametime(), ship.get_owner()->player_number(), ship.serial()));846 new CmdShipSink(get_gametime(), ship.get_owner()->player_number(), ship.serial()));
848}847}
849848
850void Game::send_player_cancel_expedition_ship(Ship& ship) {849void Game::send_player_cancel_expedition_ship(Ship& ship) {
851 send_player_command(*new CmdShipCancelExpedition(850 send_player_command(new CmdShipCancelExpedition(
852 get_gametime(), ship.get_owner()->player_number(), ship.serial()));851 get_gametime(), ship.get_owner()->player_number(), ship.serial()));
853}852}
854853
@@ -856,7 +855,7 @@
856 auto* object = objects().get_object(trade.initiator);855 auto* object = objects().get_object(trade.initiator);
857 assert(object != nullptr);856 assert(object != nullptr);
858 send_player_command(857 send_player_command(
859 *new CmdProposeTrade(get_gametime(), object->get_owner()->player_number(), trade));858 new CmdProposeTrade(get_gametime(), object->get_owner()->player_number(), trade));
860}859}
861860
862int Game::propose_trade(const Trade& trade) {861int Game::propose_trade(const Trade& trade) {
863862
=== modified file 'src/logic/game.h'
--- src/logic/game.h 2019-05-07 12:14:02 +0000
+++ src/logic/game.h 2019-05-25 17:10:52 +0000
@@ -248,7 +248,7 @@
248248
249 void enqueue_command(Command* const);249 void enqueue_command(Command* const);
250250
251 void send_player_command(Widelands::PlayerCommand&);251 void send_player_command(Widelands::PlayerCommand*);
252252
253 void send_player_bulldoze(PlayerImmovable&, bool recurse = false);253 void send_player_bulldoze(PlayerImmovable&, bool recurse = false);
254 void send_player_dismantle(PlayerImmovable&);254 void send_player_dismantle(PlayerImmovable&);
255255
=== modified file 'src/logic/game_controller.h'
--- src/logic/game_controller.h 2019-02-27 19:00:36 +0000
+++ src/logic/game_controller.h 2019-05-25 17:10:52 +0000
@@ -46,7 +46,9 @@
46 }46 }
4747
48 virtual void think() = 0;48 virtual void think() = 0;
49 virtual void send_player_command(Widelands::PlayerCommand&) = 0;49
50 // TODO(Klaus Halfmann): Command must be deleted once it was handled.
51 virtual void send_player_command(Widelands::PlayerCommand*) = 0;
50 virtual int32_t get_frametime() = 0;52 virtual int32_t get_frametime() = 0;
51 virtual GameType get_game_type() = 0;53 virtual GameType get_game_type() = 0;
5254
5355
=== modified file 'src/logic/replay_game_controller.cc'
--- src/logic/replay_game_controller.cc 2019-02-23 11:00:49 +0000
+++ src/logic/replay_game_controller.cc 2019-05-25 17:10:52 +0000
@@ -60,7 +60,7 @@
60 }60 }
61}61}
6262
63void ReplayGameController::send_player_command(Widelands::PlayerCommand&) {63void ReplayGameController::send_player_command(Widelands::PlayerCommand*) {
64 throw wexception("Trying to send a player command during replay");64 throw wexception("Trying to send a player command during replay");
65}65}
6666
6767
=== modified file 'src/logic/replay_game_controller.h'
--- src/logic/replay_game_controller.h 2019-02-23 11:00:49 +0000
+++ src/logic/replay_game_controller.h 2019-05-25 17:10:52 +0000
@@ -35,7 +35,7 @@
3535
36 void think() override;36 void think() override;
3737
38 void send_player_command(Widelands::PlayerCommand&) override;38 void send_player_command(Widelands::PlayerCommand*) override;
39 int32_t get_frametime() override;39 int32_t get_frametime() override;
40 GameController::GameType get_game_type() override;40 GameController::GameType get_game_type() override;
41 uint32_t real_speed() override;41 uint32_t real_speed() override;
4242
=== modified file 'src/logic/single_player_game_controller.cc'
--- src/logic/single_player_game_controller.cc 2019-02-23 11:00:49 +0000
+++ src/logic/single_player_game_controller.cc 2019-05-25 17:10:52 +0000
@@ -74,9 +74,9 @@
74 }74 }
75}75}
7676
77void SinglePlayerGameController::send_player_command(Widelands::PlayerCommand& pc) {77void SinglePlayerGameController::send_player_command(Widelands::PlayerCommand* pc) {
78 pc.set_cmdserial(++player_cmdserial_);78 pc->set_cmdserial(++player_cmdserial_);
79 game_.enqueue_command(&pc);79 game_.enqueue_command(pc);
80}80}
8181
82int32_t SinglePlayerGameController::get_frametime() {82int32_t SinglePlayerGameController::get_frametime() {
8383
=== modified file 'src/logic/single_player_game_controller.h'
--- src/logic/single_player_game_controller.h 2019-02-27 19:00:36 +0000
+++ src/logic/single_player_game_controller.h 2019-05-25 17:10:52 +0000
@@ -30,7 +30,7 @@
30 ~SinglePlayerGameController() override;30 ~SinglePlayerGameController() override;
3131
32 void think() override;32 void think() override;
33 void send_player_command(Widelands::PlayerCommand&) override;33 void send_player_command(Widelands::PlayerCommand*) override;
34 int32_t get_frametime() override;34 int32_t get_frametime() override;
35 GameController::GameType get_game_type() override;35 GameController::GameType get_game_type() override;
36 uint32_t real_speed() override;36 uint32_t real_speed() override;
3737
=== modified file 'src/network/gameclient.cc'
--- src/network/gameclient.cc 2019-04-29 16:22:08 +0000
+++ src/network/gameclient.cc 2019-05-25 17:10:52 +0000
@@ -58,6 +58,8 @@
58#include "wui/interactive_spectator.h"58#include "wui/interactive_spectator.h"
5959
60struct GameClientImpl {60struct GameClientImpl {
61 bool internet_;
62
61 GameSettings settings;63 GameSettings settings;
6264
63 std::string localplayername;65 std::string localplayername;
@@ -91,13 +93,121 @@
9193
92 /// Backlog of chat messages94 /// Backlog of chat messages
93 std::vector<ChatMessage> chatmessages;95 std::vector<ChatMessage> chatmessages;
96
97 /** File that is eventually transferred via the network if not found at the other side */
98 std::unique_ptr<NetTransferFile> file_;
99
100 void send_hello();
101 void send_player_command(Widelands::PlayerCommand*);
102
103 bool run_map_menu(GameClient* parent);
104 void run_game(InteractiveGameBase* igb, UI::ProgressWindow*);
105
106 InteractiveGameBase* init_game(GameClient* parent, UI::ProgressWindow*);
107
94};108};
95109
110void GameClientImpl::send_hello() {
111 SendPacket s;
112 s.unsigned_8(NETCMD_HELLO);
113 s.unsigned_8(NETWORK_PROTOCOL_VERSION);
114 s.string(localplayername);
115 s.string(build_id());
116 net->send(s);
117}
118
119void GameClientImpl::send_player_command(Widelands::PlayerCommand* pc) {
120 SendPacket s;
121 s.unsigned_8(NETCMD_PLAYERCOMMAND);
122 s.signed_32(game->get_gametime());
123 pc->serialize(s);
124 net->send(s);
125}
126
127/**
128 * Show and run() the fullscreen menu for setting map and mapsettings.
129 *
130 * @return true to indicate that run is done.
131 */
132bool GameClientImpl::run_map_menu(GameClient* parent) {
133 FullscreenMenuLaunchMPG lgm(parent, parent);
134 lgm.set_chat_provider(*parent);
135 modal = &lgm;
136 FullscreenMenuBase::MenuTarget code = lgm.run<FullscreenMenuBase::MenuTarget>();
137 modal = nullptr;
138 if (code == FullscreenMenuBase::MenuTarget::kBack) {
139 // if this is an internet game, tell the metaserver that client is back in the lobby.
140 if (internet_) {
141 InternetGaming::ref().set_game_done();
142 }
143 return true;
144 }
145 return false;
146}
147
148/**
149 * Show progress dialog and load map or saved game.
150 */
151InteractiveGameBase* GameClientImpl::init_game(GameClient* parent, UI::ProgressWindow* loader) {
152
153 const std::string& tribename = parent->get_players_tribe();
154 assert(Widelands::tribe_exists(tribename));
155 GameTips tips(*loader, {"general_game", "multiplayer", tribename});
156
157 modal = loader;
158
159 loader->step(_("Preparing game"));
160
161 game->set_game_controller(parent);
162 uint8_t const pn = settings.playernum + 1;
163 game->save_handler().set_autosave_filename(
164 (boost::format("%s_netclient%u") % kAutosavePrefix % static_cast<unsigned int>(pn)).str());
165 InteractiveGameBase* igb;
166 if (pn > 0) {
167 igb = new InteractivePlayer(*game, g_options.pull_section("global"), pn, true);
168 } else {
169 igb = new InteractiveSpectator(*game, g_options.pull_section("global"), true);
170 }
171 game -> set_ibase(igb);
172 igb->set_chat_provider(*parent);
173 if (settings.savegame) { // savegame
174 game->init_savegame(loader, settings);
175 } else { // new map
176 game->init_newgame(loader, settings);
177 }
178 return igb;
179}
180
181
182/**
183 * Run the actual game and cleanup when done.
184 */
185void GameClientImpl::run_game(InteractiveGameBase* igb, UI::ProgressWindow* loader) {
186 time.reset(game->get_gametime());
187 lasttimestamp = game->get_gametime();
188 lasttimestamp_realtime = SDL_GetTicks();
189
190 modal = igb;
191 game->run(loader, settings.savegame ?
192 Widelands::Game::Loaded :
193 settings.scenario ? Widelands::Game::NewMPScenario : Widelands::Game::NewNonScenario,
194 "", false, (boost::format("netclient_%d") % static_cast<int>(settings.usernum)).str());
195
196 // if this is an internet game, tell the metaserver that the game is done.
197 if (internet_) {
198 InternetGaming::ref().set_game_done();
199 }
200 modal = nullptr;
201 game = nullptr;
202}
203
96GameClient::GameClient(const std::pair<NetAddress, NetAddress>& host,204GameClient::GameClient(const std::pair<NetAddress, NetAddress>& host,
97 const std::string& playername,205 const std::string& playername,
98 bool internet,206 bool internet,
99 const std::string& gamename)207 const std::string& gamename)
100 : d(new GameClientImpl), internet_(internet) {208 : d(new GameClientImpl) {
209
210 d->internet_ = internet;
101211
102 if (internet) {212 if (internet) {
103 assert(!gamename.empty());213 assert(!gamename.empty());
@@ -127,7 +237,7 @@
127 d->game = nullptr;237 d->game = nullptr;
128 d->realspeed = 0;238 d->realspeed = 0;
129 d->desiredspeed = 1000;239 d->desiredspeed = 1000;
130 file_ = nullptr;240 d->file_ = nullptr;
131241
132 // Get the default win condition script242 // Get the default win condition script
133 d->settings.win_condition_script = d->settings.win_condition_scripts.front();243 d->settings.win_condition_script = d->settings.win_condition_scripts.front();
@@ -141,94 +251,41 @@
141 delete d;251 delete d;
142}252}
143253
254
255
144void GameClient::run() {256void GameClient::run() {
145 SendPacket s;
146 s.unsigned_8(NETCMD_HELLO);
147 s.unsigned_8(NETWORK_PROTOCOL_VERSION);
148 s.string(d->localplayername);
149 s.string(build_id());
150 d->net->send(s);
151257
258 d->send_hello();
152 d->settings.multiplayer = true;259 d->settings.multiplayer = true;
153260
154 // Fill the list of possible system messages261 // Fill the list of possible system messages
155 NetworkGamingMessages::fill_map();262 NetworkGamingMessages::fill_map();
156 {263
157 FullscreenMenuLaunchMPG lgm(this, this);264 if (d->run_map_menu(this)) {
158 lgm.set_chat_provider(*this);265 return; // did not select a Map ...
159 d->modal = &lgm;
160 FullscreenMenuBase::MenuTarget code = lgm.run<FullscreenMenuBase::MenuTarget>();
161 d->modal = nullptr;
162 if (code == FullscreenMenuBase::MenuTarget::kBack) {
163 // if this is an internet game, tell the metaserver that client is back in the lobby.
164 if (internet_)
165 InternetGaming::ref().set_game_done();
166 return;
167 }
168 }266 }
169267
170 d->server_is_waiting = true;268 d->server_is_waiting = true;
171269
270 bool write_sync_streams = g_options.pull_section("global").get_bool("write_syncstreams", true);
172 Widelands::Game game;271 Widelands::Game game;
173 game.set_write_syncstream(g_options.pull_section("global").get_bool("write_syncstreams", true));272 game.set_write_syncstream(write_sync_streams);
174273
175 try {274 try {
176 std::unique_ptr<UI::ProgressWindow> loader_ui(new UI::ProgressWindow());275 std::unique_ptr<UI::ProgressWindow> loader_ui(new UI::ProgressWindow());
177 d->modal = loader_ui.get();
178 std::vector<std::string> tipstext;
179 tipstext.push_back("general_game");
180 tipstext.push_back("multiplayer");
181 try {
182 tipstext.push_back(get_players_tribe());
183 } catch (NoTribe) {
184 }
185 GameTips tips(*loader_ui.get(), tipstext);
186
187 loader_ui->step(_("Preparing game"));
188276
189 d->game = &game;277 d->game = &game;
190 game.set_game_controller(this);278 InteractiveGameBase* igb = d->init_game(this, loader_ui.get());
191 uint8_t const pn = d->settings.playernum + 1;279 d->run_game(igb, loader_ui.get());
192 game.save_handler().set_autosave_filename(280
193 (boost::format("%s_netclient%u") % kAutosavePrefix % static_cast<unsigned int>(pn)).str());
194 InteractiveGameBase* igb;
195 if (pn > 0)
196 igb = new InteractivePlayer(game, g_options.pull_section("global"), pn, true);
197 else
198 igb = new InteractiveSpectator(game, g_options.pull_section("global"), true);
199 game.set_ibase(igb);
200 igb->set_chat_provider(*this);
201 if (!d->settings.savegame) { // new map
202 game.init_newgame(loader_ui.get(), d->settings);
203 } else { // savegame
204 game.init_savegame(loader_ui.get(), d->settings);
205 }
206 d->time.reset(game.get_gametime());
207 d->lasttimestamp = game.get_gametime();
208 d->lasttimestamp_realtime = SDL_GetTicks();
209
210 d->modal = igb;
211 game.run(
212 loader_ui.get(),
213 d->settings.savegame ?
214 Widelands::Game::Loaded :
215 d->settings.scenario ? Widelands::Game::NewMPScenario : Widelands::Game::NewNonScenario,
216 "", false, (boost::format("netclient_%d") % static_cast<int>(d->settings.usernum)).str());
217
218 // if this is an internet game, tell the metaserver that the game is done.
219 if (internet_)
220 InternetGaming::ref().set_game_done();
221 d->modal = nullptr;
222 d->game = nullptr;
223 } catch (...) {281 } catch (...) {
224 WLApplication::emergency_save(game);282 WLApplication::emergency_save(game);
225 d->game = nullptr;283 d->game = nullptr;
226 disconnect("CLIENT_CRASHED");284 disconnect("CLIENT_CRASHED");
227 // We will bounce back to the main menu, so we better log out285 // We will bounce back to the main menu, so we better log out
228 if (internet_) {286 if (d->internet_) {
229 InternetGaming::ref().logout("CLIENT_CRASHED");287 InternetGaming::ref().logout("CLIENT_CRASHED");
230 }288 }
231 d->modal = nullptr;
232 throw;289 throw;
233 }290 }
234}291}
@@ -237,6 +294,7 @@
237 handle_network();294 handle_network();
238295
239 if (d->game) {296 if (d->game) {
297 // TODO(Klaus Halfmann): what kind of time tricks are done here?
240 if (d->realspeed == 0 || d->server_is_waiting)298 if (d->realspeed == 0 || d->server_is_waiting)
241 d->time.fastforward();299 d->time.fastforward();
242 else300 else
@@ -254,25 +312,28 @@
254 }312 }
255}313}
256314
257void GameClient::send_player_command(Widelands::PlayerCommand& pc) {315/**
316 * Send PlayerCommand to server.
317 *
318 * @param pc will always be deleted in the end.
319 */
320void GameClient::send_player_command(Widelands::PlayerCommand* pc) {
258 assert(d->game);321 assert(d->game);
259 if (pc.sender() != d->settings.playernum + 1) {322
260 delete &pc;323 // TODDO(Klaus Halfmann)should this be an assert?
261 return;324 if (pc->sender() == d->settings.playernum + 1) // allow command for current player only
325 {
326 log("[Client]: send playercommand at time %i\n", d->game->get_gametime());
327
328 d->send_player_command(pc);
329
330 d->lasttimestamp = d->game->get_gametime();
331 d->lasttimestamp_realtime = SDL_GetTicks();
332 } else {
333 log("[Client]: Playercommand is not for current player? %i\n", pc -> sender());
262 }334 }
263335
264 log("[Client]: send playercommand at time %i\n", d->game->get_gametime());336 delete pc;
265
266 SendPacket s;
267 s.unsigned_8(NETCMD_PLAYERCOMMAND);
268 s.signed_32(d->game->get_gametime());
269 pc.serialize(s);
270 d->net->send(s);
271
272 d->lasttimestamp = d->game->get_gametime();
273 d->lasttimestamp_realtime = SDL_GetTicks();
274
275 delete &pc;
276}337}
277338
278int32_t GameClient::get_frametime() {339int32_t GameClient::get_frametime() {
@@ -544,6 +605,333 @@
544 }605 }
545}606}
546607
608void GameClient::handle_disconnect(RecvPacket& packet) {
609 uint8_t number = packet.unsigned_8();
610 std::string reason = packet.string();
611 if (number == 1)
612 disconnect(reason, "", false);
613 else {
614 std::string arg = packet.string();
615 disconnect(reason, arg, false);
616 }
617}
618
619/**
620 * Hello from the other side
621 */
622void GameClient::handle_hello(RecvPacket& packet) {
623 if (d->settings.usernum != -2) // TODO(Klaus Halfmann): if the host is the client ?.
624 throw ProtocolException(NETCMD_HELLO); // I am talkimg with myself? Bad idea
625 uint8_t const version = packet.unsigned_8();
626 if (version != NETWORK_PROTOCOL_VERSION)
627 throw DisconnectException("DIFFERENT_PROTOCOL_VERS");
628 d->settings.usernum = packet.unsigned_32(); // TODO(Klaus Halfmann): usernum is int8_t.
629 d->settings.playernum = -1;
630}
631
632/**
633 * Give a pong for a ping
634 */
635void GameClient::handle_ping(RecvPacket&) {
636 SendPacket s;
637 s.unsigned_8(NETCMD_PONG);
638 d->net->send(s);
639
640 log("[Client] Pong!\n");
641}
642
643/**
644 * New Map name was sent.
645 */
646void GameClient::handle_setting_map(RecvPacket& packet) {
647 d->settings.mapname = packet.string();
648 d->settings.mapfilename = g_fs->FileSystem::fix_cross_file(packet.string());
649 d->settings.savegame = packet.unsigned_8() == 1;
650 d->settings.scenario = packet.unsigned_8() == 1;
651 log("[Client] SETTING_MAP '%s' '%s'\n", d->settings.mapname.c_str(),
652 d->settings.mapfilename.c_str());
653
654 // New map was set, so we clean up the buffer of a previously requested file
655 d->file_.reset(nullptr);
656}
657
658/**
659 *
660 */
661// TODO(Klaus Halfmann): refactor this until it can be understood, move into impl.
662void GameClient::handle_new_file(RecvPacket& packet) {
663 std::string path = g_fs->FileSystem::fix_cross_file(packet.string());
664 uint32_t bytes = packet.unsigned_32();
665 std::string md5 = packet.string();
666
667 // Check whether the file or a file with that name already exists
668 if (g_fs->file_exists(path)) {
669 // If the file is a directory, we have to rename the file and replace it with the version
670 // of the host. If it is a zipped file, we can check, whether the host and the client have
671 // got the same file.
672 if (!g_fs->is_directory(path)) {
673 FileRead fr;
674 fr.open(*g_fs, path);
675 if (bytes == fr.get_size()) {
676 std::unique_ptr<char[]> complete(new char[bytes]);
677 if (!complete) {
678 throw wexception("Out of memory");
679 }
680 fr.data_complete(complete.get(), bytes);
681 // TODO(Klaus Halfmann): compute MD5 on the fly in FileRead...
682 SimpleMD5Checksum md5sum;
683 md5sum.data(complete.get(), bytes);
684 md5sum.finish_checksum();
685 std::string localmd5 = md5sum.get_checksum().str();
686 if (localmd5 == md5)
687 // everything is alright we now have the file.
688 return;
689 }
690 }
691 // Don't overwrite the file, better rename the original one
692 try {
693 g_fs->fs_rename(path, backup_file_name(path));
694 } catch (const FileError& e) {
695 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
696 "%s\n",
697 e.what());
698 // TODO(Arty): What now? It just means the next step will fail
699 // or possibly result in some corrupt file
700 }
701 }
702
703 // Yes we need the file!
704 SendPacket s;
705 s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
706 d->net->send(s);
707
708 d->file_.reset(new NetTransferFile());
709 d->file_->bytes = bytes;
710 d->file_->filename = path;
711 d->file_->md5sum = md5;
712 size_t position = path.rfind(g_fs->file_separator(), path.size() - 2);
713 if (position != std::string::npos) {
714 path.resize(position);
715 g_fs->ensure_directory_exists(path);
716 }
717}
718
719/**
720 *
721 */
722// TODO(Klaus Halfmann): refactor this until it can be understood, move into impl.
723void GameClient::handle_file_part(RecvPacket& packet) {
724 // Only go on, if we are waiting for a file part at the moment. It can happen, that an
725 // "unrequested" part is send by the server if the map was changed just a moment ago
726 // and there was an outstanding request from the client.
727 if (!d->file_)
728 return; // silently ignore
729
730 uint32_t part = packet.unsigned_32();
731 uint32_t size = packet.unsigned_32();
732
733 // Send an answer
734 SendPacket s;
735 s.unsigned_8(NETCMD_FILE_PART);
736 s.unsigned_32(part);
737 s.string(d->file_->md5sum);
738 d->net->send(s);
739
740 FilePart fp;
741
742 char buf[NETFILEPARTSIZE];
743 assert(size <= NETFILEPARTSIZE);
744
745 // TODO(Klaus Halfmann): read directcly into FilePart?
746 if (packet.data(buf, size) != size)
747 log("Readproblem. Will try to go on anyways\n");
748 memcpy(fp.part, &buf[0], size);
749 d->file_->parts.push_back(fp);
750
751 // Write file to disk as soon as all parts arrived
752 uint32_t left = (d->file_->bytes - NETFILEPARTSIZE * part);
753 if (left <= NETFILEPARTSIZE) {
754 FileWrite fw;
755 left = d->file_->bytes;
756 uint32_t i = 0;
757 // Put all data together
758 while (left > 0) {
759 uint32_t writeout = (left > NETFILEPARTSIZE) ? NETFILEPARTSIZE : left;
760 fw.data(d->file_->parts[i].part, writeout, FileWrite::Pos::null());
761 left -= writeout;
762 ++i;
763 }
764 // Now really write the file
765 fw.write(*g_fs, d->file_->filename.c_str());
766
767 // Check for consistence
768 FileRead fr;
769 fr.open(*g_fs, d->file_->filename);
770
771 std::unique_ptr<char[]> complete(new char[d->file_->bytes]);
772
773 fr.data_complete(complete.get(), d->file_->bytes);
774 SimpleMD5Checksum md5sum;
775 md5sum.data(complete.get(), d->file_->bytes);
776 md5sum.finish_checksum();
777 std::string localmd5 = md5sum.get_checksum().str();
778 if (localmd5 != d->file_->md5sum) {
779 // Something went wrong! We have to rerequest the file.
780 s.reset();
781 s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
782 d->net->send(s);
783 // Notify the players
784 s.reset();
785 s.unsigned_8(NETCMD_CHAT);
786 s.string(_("/me 's file failed md5 checksumming."));
787 d->net->send(s);
788 try {
789 g_fs->fs_unlink(d->file_->filename);
790 } catch (const FileError& e) {
791 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
792 "%s\n",
793 e.what());
794 }
795 }
796 // Check file for validity
797 bool invalid = false;
798 if (d->settings.savegame) {
799 // Saved game check - does Widelands recognize the file as saved game?
800 Widelands::Game game;
801 try {
802 Widelands::GameLoader gl(d->file_->filename, game);
803 } catch (...) {
804 invalid = true;
805 }
806 } else {
807 // Map check - does Widelands recognize the file as map?
808 Widelands::Map map;
809 std::unique_ptr<Widelands::MapLoader> ml = map.get_correct_loader(d->file_->filename);
810 if (!ml) {
811 invalid = true;
812 }
813 }
814 if (invalid) {
815 try {
816 g_fs->fs_unlink(d->file_->filename);
817 // Restore original file, if there was one before
818 if (g_fs->file_exists(backup_file_name(d->file_->filename)))
819 g_fs->fs_rename(backup_file_name(d->file_->filename), d->file_->filename);
820 } catch (const FileError& e) {
821 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
822 "%s\n", e.what());
823 }
824 s.reset();
825 s.unsigned_8(NETCMD_CHAT);
826 s.string(_("/me checked the received file. Although md5 check summing succeeded, "
827 "I can not handle the file."));
828 d->net->send(s);
829 }
830 }
831}
832
833/**
834 *
835 */
836void GameClient::handle_setting_tribes(RecvPacket& packet) {
837 d->settings.tribes.clear();
838 for (uint8_t i = packet.unsigned_8(); i; --i) {
839 Widelands::TribeBasicInfo info = Widelands::get_tribeinfo(packet.string());
840
841 // Get initializations (we have to do this locally, for translations)
842 LuaInterface lua;
843 info.initializations.clear();
844 for (uint8_t j = packet.unsigned_8(); j > 0; --j) {
845 std::string const initialization_script = packet.string();
846 std::unique_ptr<LuaTable> t = lua.run_script(initialization_script);
847 t->do_not_warn_about_unaccessed_keys();
848 info.initializations.push_back(Widelands::TribeBasicInfo::Initialization(
849 initialization_script, t->get_string("descname"), t->get_string("tooltip")));
850 }
851 d->settings.tribes.push_back(info);
852 }
853}
854
855/**
856 *
857 */
858void GameClient::handle_setting_allplayers(RecvPacket& packet) {
859 d->settings.players.resize(packet.unsigned_8());
860 for (uint8_t i = 0; i < d->settings.players.size(); ++i) {
861 receive_one_player(i, packet);
862 }
863 // Map changes are finished here
864 Notifications::publish(NoteGameSettings(NoteGameSettings::Action::kMap));
865}
866
867/**
868 *
869 */
870void GameClient::handle_playercommand(RecvPacket& packet) {
871 if (!d->game)
872 throw DisconnectException("PLAYERCMD_WO_GAME");
873
874 int32_t const time = packet.signed_32();
875 Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(packet);
876 plcmd.set_duetime(time);
877 d->game->enqueue_command(&plcmd);
878 d->time.receive(time);
879}
880
881/**
882 *
883 */
884void GameClient::handle_syncrequest(RecvPacket& packet) {
885 if (!d->game)
886 throw DisconnectException("SYNCREQUEST_WO_GAME");
887 int32_t const time = packet.signed_32();
888 d->time.receive(time);
889 d->game->enqueue_command(new CmdNetCheckSync(time, [this] { sync_report_callback(); }));
890 d->game->report_sync_request();
891}
892
893/**
894 *
895 */
896void GameClient::handle_chat(RecvPacket& packet) {
897 ChatMessage c("");
898 c.playern = packet.signed_16();
899 c.sender = packet.string();
900 c.msg = packet.string();
901 if (packet.unsigned_8())
902 c.recipient = packet.string();
903 d->chatmessages.push_back(c);
904 Notifications::publish(c);
905}
906
907/**
908 *
909 */
910void GameClient::handle_system_message(RecvPacket& packet) {
911 const std::string code = packet.string();
912 const std::string arg1 = packet.string();
913 const std::string arg2 = packet.string();
914 const std::string arg3 = packet.string();
915 ChatMessage c(NetworkGamingMessages::get_message(code, arg1, arg2, arg3));
916 c.playern = UserSettings::none(); // == System message
917 // c.sender remains empty to indicate a system message
918 d->chatmessages.push_back(c);
919 Notifications::publish(c);
920}
921
922/**
923 *
924 */
925void GameClient::handle_desync(RecvPacket&) {
926 log("[Client] received NETCMD_INFO_DESYNC. Trying to salvage some "
927 "information for debugging.\n");
928 if (d->game) {
929 d->game->save_syncstream(true);
930 // We don't know our playernumber, so report as -1
931 d->game->report_desync(-1);
932 }
933}
934
547/**935/**
548 * Handle one packet received from the host.936 * Handle one packet received from the host.
549 *937 *
@@ -552,363 +940,80 @@
552void GameClient::handle_packet(RecvPacket& packet) {940void GameClient::handle_packet(RecvPacket& packet) {
553 uint8_t cmd = packet.unsigned_8();941 uint8_t cmd = packet.unsigned_8();
554942
555 if (cmd == NETCMD_DISCONNECT) {943 switch (cmd) {
556 uint8_t number = packet.unsigned_8();944 case NETCMD_DISCONNECT:
557 std::string reason = packet.string();945 return handle_disconnect(packet);
558 if (number == 1)946 case NETCMD_HELLO:
559 disconnect(reason, "", false);947 return handle_hello(packet);
560 else {948 case NETCMD_PING:
561 std::string arg = packet.string();949 return handle_ping(packet);
562 disconnect(reason, arg, false);950 case NETCMD_SETTING_MAP:
563 }951 return handle_setting_map(packet);
564 return;952 case NETCMD_NEW_FILE_AVAILABLE:
565 }953 return handle_new_file(packet);
566954 case NETCMD_FILE_PART:
567 if (d->settings.usernum == -2) {955 return handle_file_part(packet);
568 if (cmd != NETCMD_HELLO)956 case NETCMD_SETTING_TRIBES:
957 return handle_setting_tribes(packet);
958 case NETCMD_SETTING_ALLPLAYERS:
959 return handle_setting_allplayers(packet);
960 case NETCMD_SETTING_PLAYER: {
961 uint8_t player = packet.unsigned_8();
962 receive_one_player(player, packet);
963 }
964 break;
965 case NETCMD_SETTING_ALLUSERS: {
966 d->settings.users.resize(packet.unsigned_8());
967 for (uint32_t i = 0; i < d->settings.users.size(); ++i)
968 receive_one_user(i, packet);
969 }
970 break;
971 case NETCMD_SETTING_USER: {
972 uint32_t user = packet.unsigned_32();
973 receive_one_user(user, packet);
974 }
975 break;
976 case NETCMD_SET_PLAYERNUMBER: {
977 int32_t number = packet.signed_32();
978 d->settings.playernum = number;
979 d->settings.users.at(d->settings.usernum).position = number;
980 }
981 break;
982 case NETCMD_WIN_CONDITION:
983 d->settings.win_condition_script = g_fs->FileSystem::fix_cross_file(packet.string());
984 break;
985 case NETCMD_PEACEFUL_MODE:
986 d->settings.peaceful = packet.unsigned_8();
987 break;
988 case NETCMD_LAUNCH:
989 if (!d->modal || d->game) {
990 throw DisconnectException("UNEXPECTED_LAUNCH");
991 }
992 d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kOk);
993 break;
994 case NETCMD_SETSPEED:
995 d->realspeed = packet.unsigned_16();
996 log("[Client] speed: %u.%03u\n", d->realspeed / 1000, d->realspeed % 1000);
997 break;
998 case NETCMD_TIME:
999 d->time.receive(packet.signed_32());
1000 break;
1001 case NETCMD_WAIT:
1002 log("[Client]: server is waiting.\n");
1003 d->server_is_waiting = true;
1004 break;
1005 case NETCMD_PLAYERCOMMAND:
1006 return handle_playercommand(packet);
1007 case NETCMD_SYNCREQUEST:
1008 return handle_syncrequest(packet);
1009 case NETCMD_CHAT:
1010 return handle_chat(packet);
1011 case NETCMD_SYSTEM_MESSAGE_CODE:
1012 return handle_system_message(packet);
1013 case NETCMD_INFO_DESYNC:
1014 return handle_desync(packet);
1015 default:
569 throw ProtocolException(cmd);1016 throw ProtocolException(cmd);
570 uint8_t const version = packet.unsigned_8();
571 if (version != NETWORK_PROTOCOL_VERSION)
572 throw DisconnectException("DIFFERENT_PROTOCOL_VERS");
573 d->settings.usernum = packet.unsigned_32();
574 d->settings.playernum = -1;
575 return;
576 }
577
578 switch (cmd) {
579 case NETCMD_PING: {
580 SendPacket s;
581 s.unsigned_8(NETCMD_PONG);
582 d->net->send(s);
583
584 log("[Client] Pong!\n");
585 break;
586 }
587
588 case NETCMD_SETTING_MAP: {
589 d->settings.mapname = packet.string();
590 d->settings.mapfilename = g_fs->FileSystem::fix_cross_file(packet.string());
591 d->settings.savegame = packet.unsigned_8() == 1;
592 d->settings.scenario = packet.unsigned_8() == 1;
593 log("[Client] SETTING_MAP '%s' '%s'\n", d->settings.mapname.c_str(),
594 d->settings.mapfilename.c_str());
595
596 // New map was set, so we clean up the buffer of a previously requested file
597 file_.reset(nullptr);
598 break;
599 }
600
601 case NETCMD_NEW_FILE_AVAILABLE: {
602 std::string path = g_fs->FileSystem::fix_cross_file(packet.string());
603 uint32_t bytes = packet.unsigned_32();
604 std::string md5 = packet.string();
605
606 // Check whether the file or a file with that name already exists
607 if (g_fs->file_exists(path)) {
608 // If the file is a directory, we have to rename the file and replace it with the version
609 // of the
610 // host. If it is a ziped file, we can check, whether the host and the client have got the
611 // same file.
612 if (!g_fs->is_directory(path)) {
613 FileRead fr;
614 fr.open(*g_fs, path);
615 if (bytes == fr.get_size()) {
616 std::unique_ptr<char[]> complete(new char[bytes]);
617 if (!complete)
618 throw wexception("Out of memory");
619
620 fr.data_complete(complete.get(), bytes);
621 SimpleMD5Checksum md5sum;
622 md5sum.data(complete.get(), bytes);
623 md5sum.finish_checksum();
624 std::string localmd5 = md5sum.get_checksum().str();
625 if (localmd5 == md5)
626 // everything is alright we already have the file.
627 return;
628 }
629 }
630 // Don't overwrite the file, better rename the original one
631 try {
632 g_fs->fs_rename(path, backup_file_name(path));
633 } catch (const FileError& e) {
634 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
635 "%s\n",
636 e.what());
637 // TODO(Arty): What now? It just means the next step will fail
638 // or possibly result in some corrupt file
639 }
640 }
641
642 // Yes we need the file!
643 SendPacket s;
644 s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
645 d->net->send(s);
646
647 file_.reset(new NetTransferFile());
648 file_->bytes = bytes;
649 file_->filename = path;
650 file_->md5sum = md5;
651 size_t position = path.rfind(g_fs->file_separator(), path.size() - 2);
652 if (position != std::string::npos) {
653 path.resize(position);
654 g_fs->ensure_directory_exists(path);
655 }
656 break;
657 }
658
659 case NETCMD_FILE_PART: {
660 // Only go on, if we are waiting for a file part at the moment. It can happen, that an
661 // "unrequested"
662 // part is send by the server if the map was changed just a moment ago and there was an
663 // outstanding
664 // request from the client.
665 if (!file_)
666 return; // silently ignore
667
668 uint32_t part = packet.unsigned_32();
669 uint32_t size = packet.unsigned_32();
670
671 // Send an answer
672 SendPacket s;
673 s.unsigned_8(NETCMD_FILE_PART);
674 s.unsigned_32(part);
675 s.string(file_->md5sum);
676 d->net->send(s);
677
678 FilePart fp;
679
680 char buf[NETFILEPARTSIZE];
681 assert(size <= NETFILEPARTSIZE);
682
683 if (packet.data(buf, size) != size)
684 log("Readproblem. Will try to go on anyways\n");
685 memcpy(fp.part, &buf[0], size);
686 file_->parts.push_back(fp);
687
688 // Write file to disk as soon as all parts arrived
689 uint32_t left = (file_->bytes - NETFILEPARTSIZE * part);
690 if (left <= NETFILEPARTSIZE) {
691 FileWrite fw;
692 left = file_->bytes;
693 uint32_t i = 0;
694 // Put all data together
695 while (left > 0) {
696 uint32_t writeout = (left > NETFILEPARTSIZE) ? NETFILEPARTSIZE : left;
697 fw.data(file_->parts[i].part, writeout, FileWrite::Pos::null());
698 left -= writeout;
699 ++i;
700 }
701 // Now really write the file
702 fw.write(*g_fs, file_->filename.c_str());
703
704 // Check for consistence
705 FileRead fr;
706 fr.open(*g_fs, file_->filename);
707
708 std::unique_ptr<char[]> complete(new char[file_->bytes]);
709
710 fr.data_complete(complete.get(), file_->bytes);
711 SimpleMD5Checksum md5sum;
712 md5sum.data(complete.get(), file_->bytes);
713 md5sum.finish_checksum();
714 std::string localmd5 = md5sum.get_checksum().str();
715 if (localmd5 != file_->md5sum) {
716 // Something went wrong! We have to rerequest the file.
717 s.reset();
718 s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
719 d->net->send(s);
720 // Notify the players
721 s.reset();
722 s.unsigned_8(NETCMD_CHAT);
723 s.string(_("/me 's file failed md5 checksumming."));
724 d->net->send(s);
725 try {
726 g_fs->fs_unlink(file_->filename);
727 } catch (const FileError& e) {
728 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
729 "%s\n",
730 e.what());
731 }
732 }
733 // Check file for validity
734 bool invalid = false;
735 if (d->settings.savegame) {
736 // Saved game check - does Widelands recognize the file as saved game?
737 Widelands::Game game;
738 try {
739 Widelands::GameLoader gl(file_->filename, game);
740 } catch (...) {
741 invalid = true;
742 }
743 } else {
744 // Map check - does Widelands recognize the file as map?
745 Widelands::Map map;
746 std::unique_ptr<Widelands::MapLoader> ml = map.get_correct_loader(file_->filename);
747 if (!ml)
748 invalid = true;
749 }
750 if (invalid) {
751 try {
752 g_fs->fs_unlink(file_->filename);
753 // Restore original file, if there was one before
754 if (g_fs->file_exists(backup_file_name(file_->filename)))
755 g_fs->fs_rename(backup_file_name(file_->filename), file_->filename);
756 } catch (const FileError& e) {
757 log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
758 "%s\n",
759 e.what());
760 }
761 s.reset();
762 s.unsigned_8(NETCMD_CHAT);
763 s.string(_("/me checked the received file. Although md5 check summing succeeded, "
764 "I can not handle the file."));
765 d->net->send(s);
766 }
767 }
768 break;
769 }
770
771 case NETCMD_SETTING_TRIBES: {
772 d->settings.tribes.clear();
773 for (uint8_t i = packet.unsigned_8(); i; --i) {
774 Widelands::TribeBasicInfo info = Widelands::get_tribeinfo(packet.string());
775
776 // Get initializations (we have to do this locally, for translations)
777 LuaInterface lua;
778 info.initializations.clear();
779 for (uint8_t j = packet.unsigned_8(); j; --j) {
780 std::string const initialization_script = packet.string();
781 std::unique_ptr<LuaTable> t = lua.run_script(initialization_script);
782 t->do_not_warn_about_unaccessed_keys();
783 info.initializations.push_back(Widelands::TribeBasicInfo::Initialization(
784 initialization_script, t->get_string("descname"), t->get_string("tooltip")));
785 }
786 d->settings.tribes.push_back(info);
787 }
788 break;
789 }
790
791 case NETCMD_SETTING_ALLPLAYERS: {
792 d->settings.players.resize(packet.unsigned_8());
793 for (uint8_t i = 0; i < d->settings.players.size(); ++i) {
794 receive_one_player(i, packet);
795 }
796 // Map changes are finished here
797 Notifications::publish(NoteGameSettings(NoteGameSettings::Action::kMap));
798 break;
799 }
800 case NETCMD_SETTING_PLAYER: {
801 uint8_t player = packet.unsigned_8();
802 receive_one_player(player, packet);
803 break;
804 }
805 case NETCMD_SETTING_ALLUSERS: {
806 d->settings.users.resize(packet.unsigned_8());
807 for (uint32_t i = 0; i < d->settings.users.size(); ++i)
808 receive_one_user(i, packet);
809 break;
810 }
811 case NETCMD_SETTING_USER: {
812 uint32_t user = packet.unsigned_32();
813 receive_one_user(user, packet);
814 break;
815 }
816 case NETCMD_SET_PLAYERNUMBER: {
817 int32_t number = packet.signed_32();
818 d->settings.playernum = number;
819 d->settings.users.at(d->settings.usernum).position = number;
820 break;
821 }
822 case NETCMD_WIN_CONDITION: {
823 d->settings.win_condition_script = g_fs->FileSystem::fix_cross_file(packet.string());
824 break;
825 }
826 case NETCMD_PEACEFUL_MODE: {
827 d->settings.peaceful = packet.unsigned_8();
828 break;
829 }
830
831 case NETCMD_LAUNCH: {
832 if (!d->modal || d->game) {
833 throw DisconnectException("UNEXPECTED_LAUNCH");
834 }
835 d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kOk);
836 break;
837 }
838
839 case NETCMD_SETSPEED:
840 d->realspeed = packet.unsigned_16();
841 log("[Client] speed: %u.%03u\n", d->realspeed / 1000, d->realspeed % 1000);
842 break;
843
844 case NETCMD_TIME:
845 d->time.receive(packet.signed_32());
846 break;
847
848 case NETCMD_WAIT:
849 log("[Client]: server is waiting.\n");
850 d->server_is_waiting = true;
851 break;
852
853 case NETCMD_PLAYERCOMMAND: {
854 if (!d->game)
855 throw DisconnectException("PLAYERCMD_WO_GAME");
856
857 int32_t const time = packet.signed_32();
858 Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(packet);
859 plcmd.set_duetime(time);
860 d->game->enqueue_command(&plcmd);
861 d->time.receive(time);
862 break;
863 }
864
865 case NETCMD_SYNCREQUEST: {
866 if (!d->game)
867 throw DisconnectException("SYNCREQUEST_WO_GAME");
868 int32_t const time = packet.signed_32();
869 d->time.receive(time);
870 d->game->enqueue_command(new CmdNetCheckSync(time, [this] { sync_report_callback(); }));
871 d->game->report_sync_request();
872 break;
873 }
874
875 case NETCMD_CHAT: {
876 ChatMessage c("");
877 c.playern = packet.signed_16();
878 c.sender = packet.string();
879 c.msg = packet.string();
880 if (packet.unsigned_8())
881 c.recipient = packet.string();
882 d->chatmessages.push_back(c);
883 Notifications::publish(c);
884 break;
885 }
886
887 case NETCMD_SYSTEM_MESSAGE_CODE: {
888 const std::string code = packet.string();
889 const std::string arg1 = packet.string();
890 const std::string arg2 = packet.string();
891 const std::string arg3 = packet.string();
892 ChatMessage c(NetworkGamingMessages::get_message(code, arg1, arg2, arg3));
893 c.playern = UserSettings::none(); // == System message
894 // c.sender remains empty to indicate a system message
895 d->chatmessages.push_back(c);
896 Notifications::publish(c);
897 break;
898 }
899
900 case NETCMD_INFO_DESYNC:
901 log("[Client] received NETCMD_INFO_DESYNC. Trying to salvage some "
902 "information for debugging.\n");
903 if (d->game) {
904 d->game->save_syncstream(true);
905 // We don't know our playernumber, so report as -1
906 d->game->report_desync(-1);
907 }
908 break;
909
910 default:
911 throw ProtocolException(cmd);
912 }1017 }
913}1018}
9141019
@@ -917,7 +1022,7 @@
917 */1022 */
918void GameClient::handle_network() {1023void GameClient::handle_network() {
919 // if this is an internet game, handle the metaserver network1024 // if this is an internet game, handle the metaserver network
920 if (internet_)1025 if (d->internet_)
921 InternetGaming::ref().handle_metaserver_communication();1026 InternetGaming::ref().handle_metaserver_communication();
922 try {1027 try {
923 assert(d->net != nullptr);1028 assert(d->net != nullptr);
@@ -965,7 +1070,7 @@
9651070
966 bool const trysave = showmsg && d->game;1071 bool const trysave = showmsg && d->game;
9671072
968 if (showmsg) {1073 if (showmsg && d->modal) { // can only show a message with a valid modal parent window
969 std::string msg;1074 std::string msg;
970 if (arg.empty())1075 if (arg.empty())
971 msg = NetworkGamingMessages::get_message(reason);1076 msg = NetworkGamingMessages::get_message(reason);
@@ -985,8 +1090,9 @@
985 if (trysave)1090 if (trysave)
986 WLApplication::emergency_save(*d->game);1091 WLApplication::emergency_save(*d->game);
9871092
1093 // TODO(Klaus Halfmann): Some of the modal windows are now handled by unique_ptr resulting in a double free.
988 if (d->modal) {1094 if (d->modal) {
989 d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kBack);1095 d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kBack);
990 d->modal = nullptr;
991 }1096 }
1097 d->modal = nullptr;
992}1098}
9931099
=== modified file 'src/network/gameclient.h'
--- src/network/gameclient.h 2019-04-29 16:22:08 +0000
+++ src/network/gameclient.h 2019-05-25 17:10:52 +0000
@@ -29,14 +29,16 @@
29#include "network/netclient_interface.h"29#include "network/netclient_interface.h"
3030
31struct GameClientImpl;31struct GameClientImpl;
32class InteractiveGameBase;
3233
33// TODO(unknown): Use composition instead of inheritance
34/**34/**
35 * GameClient manages the lifetime of a network game in which this computer35 * GameClient manages the lifetime of a network game in which this computer
36 * participates as a client.36 * participates as a client.
37 *37 *
38 * This includes running the game setup screen and the actual game after38 * This includes running the game setup screen and the actual game after
39 * launch, as well as dealing with the actual network protocol.39 * launch, as well as dealing with the actual network protocol.
40 *
41 * @param internet TODO(Klaus Halfmann): true: coonnect into the open internet via proxy, false connect locally / via IP.
40 */42 */
41struct GameClient : public GameController, public GameSettingsProvider, public ChatProvider {43struct GameClient : public GameController, public GameSettingsProvider, public ChatProvider {
42 GameClient(const std::pair<NetAddress, NetAddress>& host,44 GameClient(const std::pair<NetAddress, NetAddress>& host,
@@ -50,7 +52,7 @@
5052
51 // GameController interface53 // GameController interface
52 void think() override;54 void think() override;
53 void send_player_command(Widelands::PlayerCommand&) override;55 void send_player_command(Widelands::PlayerCommand*) override;
54 int32_t get_frametime() override;56 int32_t get_frametime() override;
55 GameController::GameType get_game_type() override;57 GameController::GameType get_game_type() override;
5658
@@ -115,7 +117,21 @@
115117
116 void sync_report_callback();118 void sync_report_callback();
117119
118 void handle_packet(RecvPacket&);120 void handle_hello(RecvPacket& packet);
121 void handle_disconnect(RecvPacket& packet);
122 void handle_ping(RecvPacket& packet);
123 void handle_new_file(RecvPacket& packet);
124 void handle_syncrequest(RecvPacket& packet);
125 void handle_setting_map(RecvPacket& packet);
126 void handle_file_part(RecvPacket& packet);
127 void handle_setting_tribes(RecvPacket& packet);
128 void handle_setting_allplayers(RecvPacket& packet);
129 void handle_playercommand(RecvPacket& packet);
130 void handle_chat(RecvPacket& packet);
131 void handle_system_message(RecvPacket& packet);
132 void handle_desync(RecvPacket& packet);
133 void handle_packet(RecvPacket& packet);
134
119 void handle_network();135 void handle_network();
120 void send_time();136 void send_time();
121 void receive_one_player(uint8_t number, StreamRead&);137 void receive_one_player(uint8_t number, StreamRead&);
@@ -125,9 +141,9 @@
125 bool sendreason = true,141 bool sendreason = true,
126 bool showmsg = true);142 bool showmsg = true);
127143
128 std::unique_ptr<NetTransferFile> file_;144
129 GameClientImpl* d;145 GameClientImpl* d;
130 bool internet_;146
131};147};
132148
133#endif // end of include guard: WL_NETWORK_GAMECLIENT_H149#endif // end of include guard: WL_NETWORK_GAMECLIENT_H
134150
=== modified file 'src/network/gamehost.cc'
--- src/network/gamehost.cc 2019-05-11 22:55:40 +0000
+++ src/network/gamehost.cc 2019-05-25 17:10:52 +0000
@@ -761,15 +761,15 @@
761 }761 }
762}762}
763763
764void GameHost::send_player_command(Widelands::PlayerCommand& pc) {764void GameHost::send_player_command(Widelands::PlayerCommand* pc) {
765 pc.set_duetime(d->committed_networktime + 1);765 pc->set_duetime(d->committed_networktime + 1);
766766
767 SendPacket packet;767 SendPacket packet;
768 packet.unsigned_8(NETCMD_PLAYERCOMMAND);768 packet.unsigned_8(NETCMD_PLAYERCOMMAND);
769 packet.signed_32(pc.duetime());769 packet.signed_32(pc->duetime());
770 pc.serialize(packet);770 pc-> serialize(packet);
771 broadcast(packet);771 broadcast(packet);
772 d->game->enqueue_command(&pc);772 d->game->enqueue_command(pc);
773773
774 committed_network_time(d->committed_networktime + 1);774 committed_network_time(d->committed_networktime + 1);
775}775}
@@ -2181,11 +2181,11 @@
2181 if (!d->game)2181 if (!d->game)
2182 throw DisconnectException("PLAYERCMD_WO_GAME");2182 throw DisconnectException("PLAYERCMD_WO_GAME");
2183 int32_t time = r.signed_32();2183 int32_t time = r.signed_32();
2184 Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(r);2184 Widelands::PlayerCommand* plcmd = Widelands::PlayerCommand::deserialize(r);
2185 log("[Host]: Client %u (%u) sent player command %u for %u, time = %i\n", i, client.playernum,2185 log("[Host]: Client %u (%u) sent player command %u for %u, time = %i\n", i, client.playernum,
2186 static_cast<unsigned int>(plcmd.id()), plcmd.sender(), time);2186 static_cast<unsigned int>(plcmd->id()), plcmd->sender(), time);
2187 receive_client_time(i, time);2187 receive_client_time(i, time);
2188 if (plcmd.sender() != client.playernum + 1)2188 if (plcmd->sender() != client.playernum + 1)
2189 throw DisconnectException("PLAYERCMD_FOR_OTHER");2189 throw DisconnectException("PLAYERCMD_FOR_OTHER");
2190 send_player_command(plcmd);2190 send_player_command(plcmd);
2191 } break;2191 } break;
21922192
=== modified file 'src/network/gamehost.h'
--- src/network/gamehost.h 2019-05-04 10:47:44 +0000
+++ src/network/gamehost.h 2019-05-25 17:10:52 +0000
@@ -50,7 +50,7 @@
5050
51 // GameController interface51 // GameController interface
52 void think() override;52 void think() override;
53 void send_player_command(Widelands::PlayerCommand&) override;53 void send_player_command(Widelands::PlayerCommand*) override;
54 int32_t get_frametime() override;54 int32_t get_frametime() override;
55 GameController::GameType get_game_type() override;55 GameController::GameType get_game_type() override;
5656
5757
=== modified file 'src/network/netclient_interface.h'
--- src/network/netclient_interface.h 2019-02-23 11:00:49 +0000
+++ src/network/netclient_interface.h 2019-05-25 17:10:52 +0000
@@ -27,6 +27,7 @@
27/**27/**
28 * NetClient manages the network connection for a network game in which this computer28 * NetClient manages the network connection for a network game in which this computer
29 * participates as a client.29 * participates as a client.
30 *
30 * This class provides the interface all NetClient implementation have to follow.31 * This class provides the interface all NetClient implementation have to follow.
31 * Currently two implementations exists: A "real" NetClient for local games and a32 * Currently two implementations exists: A "real" NetClient for local games and a
32 * NetClientProxy which relays commands over a relay server.33 * NetClientProxy which relays commands over a relay server.
@@ -42,18 +43,21 @@
4243
43 /**44 /**
44 * Returns whether the client is connected.45 * Returns whether the client is connected.
46 *
45 * \return \c true if the connection is open, \c false otherwise.47 * \return \c true if the connection is open, \c false otherwise.
46 */48 */
47 virtual bool is_connected() const = 0;49 virtual bool is_connected() const = 0;
4850
49 /**51 /**
50 * Closes the connection.52 * Closes the connection.
53 *
51 * If you want to send a goodbye-message to the host, do so before calling this.54 * If you want to send a goodbye-message to the host, do so before calling this.
52 */55 */
53 virtual void close() = 0;56 virtual void close() = 0;
5457
55 /**58 /**
56 * Tries to receive a packet.59 * Tries to receive a packet.
60 *
57 * \return A pointer to a packet if one packet is available, an invalid pointer otherwise.61 * \return A pointer to a packet if one packet is available, an invalid pointer otherwise.
58 * Calling this on a closed connection will return an invalid pointer.62 * Calling this on a closed connection will return an invalid pointer.
59 */63 */
@@ -61,6 +65,7 @@
6165
62 /**66 /**
63 * Sends a packet.67 * Sends a packet.
68 *
64 * Calling this on a closed connection will silently fail.69 * Calling this on a closed connection will silently fail.
65 * \param packet The packet to send.70 * \param packet The packet to send.
66 */71 */
6772
=== modified file 'src/network/nethostproxy.cc'
--- src/network/nethostproxy.cc 2018-03-03 10:48:14 +0000
+++ src/network/nethostproxy.cc 2019-05-25 17:10:52 +0000
@@ -256,7 +256,7 @@
256 conn_->receive(&cmd);256 conn_->receive(&cmd);
257 uint8_t id;257 uint8_t id;
258 conn_->receive(&id);258 conn_->receive(&id);
259 assert(clients_.count(id));259 assert(clients_.count(id)); // TODO(Klaus Halfmann): As of a race condition this may not always hold.
260 clients_.at(id).state_ = Client::State::kDisconnected;260 clients_.at(id).state_ = Client::State::kDisconnected;
261 }261 }
262 break;262 break;
263263
=== modified file 'src/wui/economy_options_window.cc'
--- src/wui/economy_options_window.cc 2019-02-23 11:00:49 +0000
+++ src/wui/economy_options_window.cc 2019-05-25 17:10:52 +0000
@@ -189,11 +189,11 @@
189 // Don't allow negative new amount.189 // Don't allow negative new amount.
190 if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) {190 if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) {
191 if (is_wares) {191 if (is_wares) {
192 game.send_player_command(*new Widelands::CmdSetWareTargetQuantity(192 game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
193 game.get_gametime(), player_->player_number(), serial_, index,193 game.get_gametime(), player_->player_number(), serial_, index,
194 tq.permanent + amount));194 tq.permanent + amount));
195 } else {195 } else {
196 game.send_player_command(*new Widelands::CmdSetWorkerTargetQuantity(196 game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
197 game.get_gametime(), player_->player_number(), serial_, index,197 game.get_gametime(), player_->player_number(), serial_, index,
198 tq.permanent + amount));198 tq.permanent + amount));
199 }199 }
@@ -209,10 +209,10 @@
209 for (const Widelands::DescriptionIndex& index : items) {209 for (const Widelands::DescriptionIndex& index : items) {
210 if (display_.ware_selected(index)) {210 if (display_.ware_selected(index)) {
211 if (is_wares) {211 if (is_wares) {
212 game.send_player_command(*new Widelands::CmdResetWareTargetQuantity(212 game.send_player_command(new Widelands::CmdResetWareTargetQuantity(
213 game.get_gametime(), player_->player_number(), serial_, index));213 game.get_gametime(), player_->player_number(), serial_, index));
214 } else {214 } else {
215 game.send_player_command(*new Widelands::CmdResetWorkerTargetQuantity(215 game.send_player_command(new Widelands::CmdResetWorkerTargetQuantity(
216 game.get_gametime(), player_->player_number(), serial_, index));216 game.get_gametime(), player_->player_number(), serial_, index));
217 }217 }
218 }218 }
219219
=== modified file 'src/wui/game_message_menu.cc'
--- src/wui/game_message_menu.cc 2019-04-18 16:50:35 +0000
+++ src/wui/game_message_menu.cc 2019-05-25 17:10:52 +0000
@@ -336,7 +336,7 @@
336 // Maybe the message was removed since think?336 // Maybe the message was removed since think?
337 if (message->status() == Message::Status::kNew) {337 if (message->status() == Message::Status::kNew) {
338 Widelands::Game& game = iplayer().game();338 Widelands::Game& game = iplayer().game();
339 game.send_player_command(*new Widelands::CmdMessageSetStatusRead(339 game.send_player_command(new Widelands::CmdMessageSetStatusRead(
340 game.get_gametime(), player.player_number(), id));340 game.get_gametime(), player.player_number(), id));
341 }341 }
342 centerviewbtn_->set_enabled(message->position());342 centerviewbtn_->set_enabled(message->position());
@@ -439,12 +439,12 @@
439 switch (mode) {439 switch (mode) {
440 case Inbox:440 case Inbox:
441 // Archive highlighted message441 // Archive highlighted message
442 game.send_player_command(*new Widelands::CmdMessageSetStatusArchived(442 game.send_player_command(new Widelands::CmdMessageSetStatusArchived(
443 game.get_gametime(), plnum, MessageId(selected_record)));443 game.get_gametime(), plnum, MessageId(selected_record)));
444 break;444 break;
445 case Archive:445 case Archive:
446 // Restore highlighted message446 // Restore highlighted message
447 game.send_player_command(*new Widelands::CmdMessageSetStatusRead(447 game.send_player_command(new Widelands::CmdMessageSetStatusRead(
448 game.get_gametime(), plnum, MessageId(selected_record)));448 game.get_gametime(), plnum, MessageId(selected_record)));
449 break;449 break;
450 }450 }
451451
=== modified file 'src/wui/warehousewindow.cc'
--- src/wui/warehousewindow.cc 2019-02-23 11:00:49 +0000
+++ src/wui/warehousewindow.cc 2019-05-25 17:10:52 +0000
@@ -160,7 +160,7 @@
160160
161 for (const Widelands::DescriptionIndex& index : indices) {161 for (const Widelands::DescriptionIndex& index : indices) {
162 if (display_.ware_selected(index)) {162 if (display_.ware_selected(index)) {
163 gb_.game().send_player_command(*new Widelands::CmdSetStockPolicy(163 gb_.game().send_player_command(new Widelands::CmdSetStockPolicy(
164 gb_.game().get_gametime(), wh_.owner().player_number(), wh_, is_workers, index,164 gb_.game().get_gametime(), wh_.owner().player_number(), wh_, is_workers, index,
165 newpolicy));165 newpolicy));
166 }166 }

Subscribers

People subscribed via source and target branches

to status/vote changes: