Merge lp:~widelands-dev/widelands/refactor_gameclient into lp:widelands
- refactor_gameclient
- Merge into trunk
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 |
Related bugs: |
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.
Klaus Halfmann (klaus-halfmann) wrote : | # |
GunChleoc (gunchleoc) : | # |
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_
* 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.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4845. State: passed. Details: https:/
Appveyor build 4626. State: success. Details: https:/
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:
* 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_
-> 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 :)
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.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Found a first bug, still need some debugging
Klaus Halfmann (klaus-halfmann) wrote : | # |
Still get a heap-use-after-free related to
std:
But this is maybe just a problem as the game crashed with disconnect(
A have a deja vue around GameClientImpl.
GunChleoc (gunchleoc) wrote : | # |
You could also do the game tips like this:
const std::string tribename = get_players_
assert(
std:
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.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4871. State: failed. Details: https:/
Appveyor build 4652. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
Using unique_
(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
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
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4891. State: passed. Details: https:/
Appveyor build 4672. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
Now why can we nort merge this one?
GunChleoc (gunchleoc) wrote : | # |
Sorry, your recent changes slipped me by. Thanks for the reminder, I will do another review & testing.
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
Preview Diff
1 | === modified file 'src/ai/defaultai.cc' | |||
2 | --- src/ai/defaultai.cc 2019-05-22 11:23:14 +0000 | |||
3 | +++ src/ai/defaultai.cc 2019-05-25 17:10:52 +0000 | |||
4 | @@ -6612,7 +6612,7 @@ | |||
5 | 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); |
6 | 6613 | assert(new_target > 1); | 6613 | assert(new_target > 1); |
7 | 6614 | 6614 | ||
9 | 6615 | game().send_player_command(*new Widelands::CmdSetWareTargetQuantity( | 6615 | game().send_player_command(new Widelands::CmdSetWareTargetQuantity( |
10 | 6616 | gametime, player_number(), observer->economy.serial(), id, new_target)); | 6616 | gametime, player_number(), observer->economy.serial(), id, new_target)); |
11 | 6617 | } | 6617 | } |
12 | 6618 | } | 6618 | } |
13 | 6619 | 6619 | ||
14 | === modified file 'src/logic/game.cc' | |||
15 | --- src/logic/game.cc 2019-05-11 18:19:20 +0000 | |||
16 | +++ src/logic/game.cc 2019-05-25 17:10:52 +0000 | |||
17 | @@ -711,7 +711,7 @@ | |||
18 | 711 | * It takes the appropriate action, i.e. either add to the cmd_queue or send | 711 | * It takes the appropriate action, i.e. either add to the cmd_queue or send |
19 | 712 | * across the network. | 712 | * across the network. |
20 | 713 | */ | 713 | */ |
22 | 714 | void Game::send_player_command(PlayerCommand& pc) { | 714 | void Game::send_player_command(PlayerCommand* pc) { |
23 | 715 | ctrl_->send_player_command(pc); | 715 | ctrl_->send_player_command(pc); |
24 | 716 | } | 716 | } |
25 | 717 | 717 | ||
26 | @@ -734,55 +734,55 @@ | |||
27 | 734 | 734 | ||
28 | 735 | // we might want to make these inlines: | 735 | // we might want to make these inlines: |
29 | 736 | void Game::send_player_bulldoze(PlayerImmovable& pi, bool const recurse) { | 736 | void Game::send_player_bulldoze(PlayerImmovable& pi, bool const recurse) { |
31 | 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)); |
32 | 738 | } | 738 | } |
33 | 739 | 739 | ||
34 | 740 | void Game::send_player_dismantle(PlayerImmovable& pi) { | 740 | void Game::send_player_dismantle(PlayerImmovable& pi) { |
36 | 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)); |
37 | 742 | } | 742 | } |
38 | 743 | 743 | ||
39 | 744 | void Game::send_player_build(int32_t const pid, const Coords& coords, DescriptionIndex const id) { | 744 | void Game::send_player_build(int32_t const pid, const Coords& coords, DescriptionIndex const id) { |
40 | 745 | assert(tribes().building_exists(id)); | 745 | assert(tribes().building_exists(id)); |
42 | 746 | send_player_command(*new CmdBuild(get_gametime(), pid, coords, id)); | 746 | send_player_command(new CmdBuild(get_gametime(), pid, coords, id)); |
43 | 747 | } | 747 | } |
44 | 748 | 748 | ||
45 | 749 | void Game::send_player_build_flag(int32_t const pid, const Coords& coords) { | 749 | void Game::send_player_build_flag(int32_t const pid, const Coords& coords) { |
47 | 750 | send_player_command(*new CmdBuildFlag(get_gametime(), pid, coords)); | 750 | send_player_command(new CmdBuildFlag(get_gametime(), pid, coords)); |
48 | 751 | } | 751 | } |
49 | 752 | 752 | ||
50 | 753 | void Game::send_player_build_road(int32_t pid, Path& path) { | 753 | void Game::send_player_build_road(int32_t pid, Path& path) { |
52 | 754 | send_player_command(*new CmdBuildRoad(get_gametime(), pid, path)); | 754 | send_player_command(new CmdBuildRoad(get_gametime(), pid, path)); |
53 | 755 | } | 755 | } |
54 | 756 | 756 | ||
55 | 757 | void Game::send_player_flagaction(Flag& flag) { | 757 | void Game::send_player_flagaction(Flag& flag) { |
57 | 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)); |
58 | 759 | } | 759 | } |
59 | 760 | 760 | ||
60 | 761 | void Game::send_player_start_stop_building(Building& building) { | 761 | void Game::send_player_start_stop_building(Building& building) { |
61 | 762 | send_player_command( | 762 | send_player_command( |
63 | 763 | *new CmdStartStopBuilding(get_gametime(), building.owner().player_number(), building)); | 763 | new CmdStartStopBuilding(get_gametime(), building.owner().player_number(), building)); |
64 | 764 | } | 764 | } |
65 | 765 | 765 | ||
66 | 766 | void Game::send_player_militarysite_set_soldier_preference(Building& building, | 766 | void Game::send_player_militarysite_set_soldier_preference(Building& building, |
67 | 767 | SoldierPreference my_preference) { | 767 | SoldierPreference my_preference) { |
69 | 768 | send_player_command(*new CmdMilitarySiteSetSoldierPreference( | 768 | send_player_command(new CmdMilitarySiteSetSoldierPreference( |
70 | 769 | get_gametime(), building.owner().player_number(), building, my_preference)); | 769 | get_gametime(), building.owner().player_number(), building, my_preference)); |
71 | 770 | } | 770 | } |
72 | 771 | 771 | ||
73 | 772 | void Game::send_player_start_or_cancel_expedition(Building& building) { | 772 | void Game::send_player_start_or_cancel_expedition(Building& building) { |
74 | 773 | send_player_command( | 773 | send_player_command( |
76 | 774 | *new CmdStartOrCancelExpedition(get_gametime(), building.owner().player_number(), building)); | 774 | new CmdStartOrCancelExpedition(get_gametime(), building.owner().player_number(), building)); |
77 | 775 | } | 775 | } |
78 | 776 | 776 | ||
79 | 777 | void Game::send_player_enhance_building(Building& building, DescriptionIndex const id) { | 777 | void Game::send_player_enhance_building(Building& building, DescriptionIndex const id) { |
80 | 778 | assert(building.owner().tribe().has_building(id)); | 778 | assert(building.owner().tribe().has_building(id)); |
81 | 779 | 779 | ||
82 | 780 | send_player_command( | 780 | send_player_command( |
84 | 781 | *new CmdEnhanceBuilding(get_gametime(), building.owner().player_number(), building, id)); | 781 | new CmdEnhanceBuilding(get_gametime(), building.owner().player_number(), building, id)); |
85 | 782 | } | 782 | } |
86 | 783 | 783 | ||
87 | 784 | void Game::send_player_evict_worker(Worker& worker) { | 784 | void Game::send_player_evict_worker(Worker& worker) { |
89 | 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)); |
90 | 786 | } | 786 | } |
91 | 787 | 787 | ||
92 | 788 | void Game::send_player_set_ware_priority(PlayerImmovable& imm, | 788 | void Game::send_player_set_ware_priority(PlayerImmovable& imm, |
93 | @@ -790,14 +790,14 @@ | |||
94 | 790 | DescriptionIndex const index, | 790 | DescriptionIndex const index, |
95 | 791 | int32_t const prio) { | 791 | int32_t const prio) { |
96 | 792 | send_player_command( | 792 | send_player_command( |
98 | 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)); |
99 | 794 | } | 794 | } |
100 | 795 | 795 | ||
101 | 796 | void Game::send_player_set_input_max_fill(PlayerImmovable& imm, | 796 | void Game::send_player_set_input_max_fill(PlayerImmovable& imm, |
102 | 797 | DescriptionIndex const index, | 797 | DescriptionIndex const index, |
103 | 798 | WareWorker type, | 798 | WareWorker type, |
104 | 799 | uint32_t const max_fill) { | 799 | uint32_t const max_fill) { |
106 | 800 | send_player_command(*new CmdSetInputMaxFill( | 800 | send_player_command(new CmdSetInputMaxFill( |
107 | 801 | get_gametime(), imm.owner().player_number(), imm, index, type, max_fill)); | 801 | get_gametime(), imm.owner().player_number(), imm, index, type, max_fill)); |
108 | 802 | } | 802 | } |
109 | 803 | 803 | ||
110 | @@ -805,50 +805,49 @@ | |||
111 | 805 | TrainingAttribute attr, | 805 | TrainingAttribute attr, |
112 | 806 | int32_t const val) { | 806 | int32_t const val) { |
113 | 807 | send_player_command( | 807 | send_player_command( |
115 | 808 | *new CmdChangeTrainingOptions(get_gametime(), ts.owner().player_number(), ts, attr, val)); | 808 | new CmdChangeTrainingOptions(get_gametime(), ts.owner().player_number(), ts, attr, val)); |
116 | 809 | } | 809 | } |
117 | 810 | 810 | ||
118 | 811 | void Game::send_player_drop_soldier(Building& b, int32_t const ser) { | 811 | void Game::send_player_drop_soldier(Building& b, int32_t const ser) { |
119 | 812 | assert(ser != -1); | 812 | assert(ser != -1); |
121 | 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)); |
122 | 814 | } | 814 | } |
123 | 815 | 815 | ||
124 | 816 | void Game::send_player_change_soldier_capacity(Building& b, int32_t const val) { | 816 | void Game::send_player_change_soldier_capacity(Building& b, int32_t const val) { |
125 | 817 | send_player_command( | 817 | send_player_command( |
127 | 818 | *new CmdChangeSoldierCapacity(get_gametime(), b.owner().player_number(), b, val)); | 818 | new CmdChangeSoldierCapacity(get_gametime(), b.owner().player_number(), b, val)); |
128 | 819 | } | 819 | } |
129 | 820 | 820 | ||
130 | 821 | void Game::send_player_enemyflagaction(const Flag& flag, | 821 | void Game::send_player_enemyflagaction(const Flag& flag, |
131 | 822 | PlayerNumber const who_attacks, | 822 | PlayerNumber const who_attacks, |
132 | 823 | const std::vector<Serial>& soldiers) { | 823 | const std::vector<Serial>& soldiers) { |
133 | 824 | if (1 < player(who_attacks) | 824 | if (1 < player(who_attacks) |
137 | 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()))) |
138 | 826 | send_player_command(*new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, soldiers)); | 826 | send_player_command(new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, soldiers)); |
136 | 827 | } | ||
139 | 828 | } | 827 | } |
140 | 829 | 828 | ||
141 | 830 | void Game::send_player_ship_scouting_direction(Ship& ship, WalkingDir direction) { | 829 | void Game::send_player_ship_scouting_direction(Ship& ship, WalkingDir direction) { |
143 | 831 | send_player_command(*new CmdShipScoutDirection( | 830 | send_player_command(new CmdShipScoutDirection( |
144 | 832 | get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction)); | 831 | get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction)); |
145 | 833 | } | 832 | } |
146 | 834 | 833 | ||
147 | 835 | void Game::send_player_ship_construct_port(Ship& ship, Coords coords) { | 834 | void Game::send_player_ship_construct_port(Ship& ship, Coords coords) { |
149 | 836 | send_player_command(*new CmdShipConstructPort( | 835 | send_player_command(new CmdShipConstructPort( |
150 | 837 | get_gametime(), ship.get_owner()->player_number(), ship.serial(), coords)); | 836 | get_gametime(), ship.get_owner()->player_number(), ship.serial(), coords)); |
151 | 838 | } | 837 | } |
152 | 839 | 838 | ||
153 | 840 | void Game::send_player_ship_explore_island(Ship& ship, IslandExploreDirection direction) { | 839 | void Game::send_player_ship_explore_island(Ship& ship, IslandExploreDirection direction) { |
155 | 841 | send_player_command(*new CmdShipExploreIsland( | 840 | send_player_command(new CmdShipExploreIsland( |
156 | 842 | get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction)); | 841 | get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction)); |
157 | 843 | } | 842 | } |
158 | 844 | 843 | ||
159 | 845 | void Game::send_player_sink_ship(Ship& ship) { | 844 | void Game::send_player_sink_ship(Ship& ship) { |
160 | 846 | send_player_command( | 845 | send_player_command( |
162 | 847 | *new CmdShipSink(get_gametime(), ship.get_owner()->player_number(), ship.serial())); | 846 | new CmdShipSink(get_gametime(), ship.get_owner()->player_number(), ship.serial())); |
163 | 848 | } | 847 | } |
164 | 849 | 848 | ||
165 | 850 | void Game::send_player_cancel_expedition_ship(Ship& ship) { | 849 | void Game::send_player_cancel_expedition_ship(Ship& ship) { |
167 | 851 | send_player_command(*new CmdShipCancelExpedition( | 850 | send_player_command(new CmdShipCancelExpedition( |
168 | 852 | get_gametime(), ship.get_owner()->player_number(), ship.serial())); | 851 | get_gametime(), ship.get_owner()->player_number(), ship.serial())); |
169 | 853 | } | 852 | } |
170 | 854 | 853 | ||
171 | @@ -856,7 +855,7 @@ | |||
172 | 856 | auto* object = objects().get_object(trade.initiator); | 855 | auto* object = objects().get_object(trade.initiator); |
173 | 857 | assert(object != nullptr); | 856 | assert(object != nullptr); |
174 | 858 | send_player_command( | 857 | send_player_command( |
176 | 859 | *new CmdProposeTrade(get_gametime(), object->get_owner()->player_number(), trade)); | 858 | new CmdProposeTrade(get_gametime(), object->get_owner()->player_number(), trade)); |
177 | 860 | } | 859 | } |
178 | 861 | 860 | ||
179 | 862 | int Game::propose_trade(const Trade& trade) { | 861 | int Game::propose_trade(const Trade& trade) { |
180 | 863 | 862 | ||
181 | === modified file 'src/logic/game.h' | |||
182 | --- src/logic/game.h 2019-05-07 12:14:02 +0000 | |||
183 | +++ src/logic/game.h 2019-05-25 17:10:52 +0000 | |||
184 | @@ -248,7 +248,7 @@ | |||
185 | 248 | 248 | ||
186 | 249 | void enqueue_command(Command* const); | 249 | void enqueue_command(Command* const); |
187 | 250 | 250 | ||
189 | 251 | void send_player_command(Widelands::PlayerCommand&); | 251 | void send_player_command(Widelands::PlayerCommand*); |
190 | 252 | 252 | ||
191 | 253 | void send_player_bulldoze(PlayerImmovable&, bool recurse = false); | 253 | void send_player_bulldoze(PlayerImmovable&, bool recurse = false); |
192 | 254 | void send_player_dismantle(PlayerImmovable&); | 254 | void send_player_dismantle(PlayerImmovable&); |
193 | 255 | 255 | ||
194 | === modified file 'src/logic/game_controller.h' | |||
195 | --- src/logic/game_controller.h 2019-02-27 19:00:36 +0000 | |||
196 | +++ src/logic/game_controller.h 2019-05-25 17:10:52 +0000 | |||
197 | @@ -46,7 +46,9 @@ | |||
198 | 46 | } | 46 | } |
199 | 47 | 47 | ||
200 | 48 | virtual void think() = 0; | 48 | virtual void think() = 0; |
202 | 49 | virtual void send_player_command(Widelands::PlayerCommand&) = 0; | 49 | |
203 | 50 | // TODO(Klaus Halfmann): Command must be deleted once it was handled. | ||
204 | 51 | virtual void send_player_command(Widelands::PlayerCommand*) = 0; | ||
205 | 50 | virtual int32_t get_frametime() = 0; | 52 | virtual int32_t get_frametime() = 0; |
206 | 51 | virtual GameType get_game_type() = 0; | 53 | virtual GameType get_game_type() = 0; |
207 | 52 | 54 | ||
208 | 53 | 55 | ||
209 | === modified file 'src/logic/replay_game_controller.cc' | |||
210 | --- src/logic/replay_game_controller.cc 2019-02-23 11:00:49 +0000 | |||
211 | +++ src/logic/replay_game_controller.cc 2019-05-25 17:10:52 +0000 | |||
212 | @@ -60,7 +60,7 @@ | |||
213 | 60 | } | 60 | } |
214 | 61 | } | 61 | } |
215 | 62 | 62 | ||
217 | 63 | void ReplayGameController::send_player_command(Widelands::PlayerCommand&) { | 63 | void ReplayGameController::send_player_command(Widelands::PlayerCommand*) { |
218 | 64 | throw wexception("Trying to send a player command during replay"); | 64 | throw wexception("Trying to send a player command during replay"); |
219 | 65 | } | 65 | } |
220 | 66 | 66 | ||
221 | 67 | 67 | ||
222 | === modified file 'src/logic/replay_game_controller.h' | |||
223 | --- src/logic/replay_game_controller.h 2019-02-23 11:00:49 +0000 | |||
224 | +++ src/logic/replay_game_controller.h 2019-05-25 17:10:52 +0000 | |||
225 | @@ -35,7 +35,7 @@ | |||
226 | 35 | 35 | ||
227 | 36 | void think() override; | 36 | void think() override; |
228 | 37 | 37 | ||
230 | 38 | void send_player_command(Widelands::PlayerCommand&) override; | 38 | void send_player_command(Widelands::PlayerCommand*) override; |
231 | 39 | int32_t get_frametime() override; | 39 | int32_t get_frametime() override; |
232 | 40 | GameController::GameType get_game_type() override; | 40 | GameController::GameType get_game_type() override; |
233 | 41 | uint32_t real_speed() override; | 41 | uint32_t real_speed() override; |
234 | 42 | 42 | ||
235 | === modified file 'src/logic/single_player_game_controller.cc' | |||
236 | --- src/logic/single_player_game_controller.cc 2019-02-23 11:00:49 +0000 | |||
237 | +++ src/logic/single_player_game_controller.cc 2019-05-25 17:10:52 +0000 | |||
238 | @@ -74,9 +74,9 @@ | |||
239 | 74 | } | 74 | } |
240 | 75 | } | 75 | } |
241 | 76 | 76 | ||
245 | 77 | void SinglePlayerGameController::send_player_command(Widelands::PlayerCommand& pc) { | 77 | void SinglePlayerGameController::send_player_command(Widelands::PlayerCommand* pc) { |
246 | 78 | pc.set_cmdserial(++player_cmdserial_); | 78 | pc->set_cmdserial(++player_cmdserial_); |
247 | 79 | game_.enqueue_command(&pc); | 79 | game_.enqueue_command(pc); |
248 | 80 | } | 80 | } |
249 | 81 | 81 | ||
250 | 82 | int32_t SinglePlayerGameController::get_frametime() { | 82 | int32_t SinglePlayerGameController::get_frametime() { |
251 | 83 | 83 | ||
252 | === modified file 'src/logic/single_player_game_controller.h' | |||
253 | --- src/logic/single_player_game_controller.h 2019-02-27 19:00:36 +0000 | |||
254 | +++ src/logic/single_player_game_controller.h 2019-05-25 17:10:52 +0000 | |||
255 | @@ -30,7 +30,7 @@ | |||
256 | 30 | ~SinglePlayerGameController() override; | 30 | ~SinglePlayerGameController() override; |
257 | 31 | 31 | ||
258 | 32 | void think() override; | 32 | void think() override; |
260 | 33 | void send_player_command(Widelands::PlayerCommand&) override; | 33 | void send_player_command(Widelands::PlayerCommand*) override; |
261 | 34 | int32_t get_frametime() override; | 34 | int32_t get_frametime() override; |
262 | 35 | GameController::GameType get_game_type() override; | 35 | GameController::GameType get_game_type() override; |
263 | 36 | uint32_t real_speed() override; | 36 | uint32_t real_speed() override; |
264 | 37 | 37 | ||
265 | === modified file 'src/network/gameclient.cc' | |||
266 | --- src/network/gameclient.cc 2019-04-29 16:22:08 +0000 | |||
267 | +++ src/network/gameclient.cc 2019-05-25 17:10:52 +0000 | |||
268 | @@ -58,6 +58,8 @@ | |||
269 | 58 | #include "wui/interactive_spectator.h" | 58 | #include "wui/interactive_spectator.h" |
270 | 59 | 59 | ||
271 | 60 | struct GameClientImpl { | 60 | struct GameClientImpl { |
272 | 61 | bool internet_; | ||
273 | 62 | |||
274 | 61 | GameSettings settings; | 63 | GameSettings settings; |
275 | 62 | 64 | ||
276 | 63 | std::string localplayername; | 65 | std::string localplayername; |
277 | @@ -91,13 +93,121 @@ | |||
278 | 91 | 93 | ||
279 | 92 | /// Backlog of chat messages | 94 | /// Backlog of chat messages |
280 | 93 | std::vector<ChatMessage> chatmessages; | 95 | std::vector<ChatMessage> chatmessages; |
281 | 96 | |||
282 | 97 | /** File that is eventually transferred via the network if not found at the other side */ | ||
283 | 98 | std::unique_ptr<NetTransferFile> file_; | ||
284 | 99 | |||
285 | 100 | void send_hello(); | ||
286 | 101 | void send_player_command(Widelands::PlayerCommand*); | ||
287 | 102 | |||
288 | 103 | bool run_map_menu(GameClient* parent); | ||
289 | 104 | void run_game(InteractiveGameBase* igb, UI::ProgressWindow*); | ||
290 | 105 | |||
291 | 106 | InteractiveGameBase* init_game(GameClient* parent, UI::ProgressWindow*); | ||
292 | 107 | |||
293 | 94 | }; | 108 | }; |
294 | 95 | 109 | ||
295 | 110 | void GameClientImpl::send_hello() { | ||
296 | 111 | SendPacket s; | ||
297 | 112 | s.unsigned_8(NETCMD_HELLO); | ||
298 | 113 | s.unsigned_8(NETWORK_PROTOCOL_VERSION); | ||
299 | 114 | s.string(localplayername); | ||
300 | 115 | s.string(build_id()); | ||
301 | 116 | net->send(s); | ||
302 | 117 | } | ||
303 | 118 | |||
304 | 119 | void GameClientImpl::send_player_command(Widelands::PlayerCommand* pc) { | ||
305 | 120 | SendPacket s; | ||
306 | 121 | s.unsigned_8(NETCMD_PLAYERCOMMAND); | ||
307 | 122 | s.signed_32(game->get_gametime()); | ||
308 | 123 | pc->serialize(s); | ||
309 | 124 | net->send(s); | ||
310 | 125 | } | ||
311 | 126 | |||
312 | 127 | /** | ||
313 | 128 | * Show and run() the fullscreen menu for setting map and mapsettings. | ||
314 | 129 | * | ||
315 | 130 | * @return true to indicate that run is done. | ||
316 | 131 | */ | ||
317 | 132 | bool GameClientImpl::run_map_menu(GameClient* parent) { | ||
318 | 133 | FullscreenMenuLaunchMPG lgm(parent, parent); | ||
319 | 134 | lgm.set_chat_provider(*parent); | ||
320 | 135 | modal = &lgm; | ||
321 | 136 | FullscreenMenuBase::MenuTarget code = lgm.run<FullscreenMenuBase::MenuTarget>(); | ||
322 | 137 | modal = nullptr; | ||
323 | 138 | if (code == FullscreenMenuBase::MenuTarget::kBack) { | ||
324 | 139 | // if this is an internet game, tell the metaserver that client is back in the lobby. | ||
325 | 140 | if (internet_) { | ||
326 | 141 | InternetGaming::ref().set_game_done(); | ||
327 | 142 | } | ||
328 | 143 | return true; | ||
329 | 144 | } | ||
330 | 145 | return false; | ||
331 | 146 | } | ||
332 | 147 | |||
333 | 148 | /** | ||
334 | 149 | * Show progress dialog and load map or saved game. | ||
335 | 150 | */ | ||
336 | 151 | InteractiveGameBase* GameClientImpl::init_game(GameClient* parent, UI::ProgressWindow* loader) { | ||
337 | 152 | |||
338 | 153 | const std::string& tribename = parent->get_players_tribe(); | ||
339 | 154 | assert(Widelands::tribe_exists(tribename)); | ||
340 | 155 | GameTips tips(*loader, {"general_game", "multiplayer", tribename}); | ||
341 | 156 | |||
342 | 157 | modal = loader; | ||
343 | 158 | |||
344 | 159 | loader->step(_("Preparing game")); | ||
345 | 160 | |||
346 | 161 | game->set_game_controller(parent); | ||
347 | 162 | uint8_t const pn = settings.playernum + 1; | ||
348 | 163 | game->save_handler().set_autosave_filename( | ||
349 | 164 | (boost::format("%s_netclient%u") % kAutosavePrefix % static_cast<unsigned int>(pn)).str()); | ||
350 | 165 | InteractiveGameBase* igb; | ||
351 | 166 | if (pn > 0) { | ||
352 | 167 | igb = new InteractivePlayer(*game, g_options.pull_section("global"), pn, true); | ||
353 | 168 | } else { | ||
354 | 169 | igb = new InteractiveSpectator(*game, g_options.pull_section("global"), true); | ||
355 | 170 | } | ||
356 | 171 | game -> set_ibase(igb); | ||
357 | 172 | igb->set_chat_provider(*parent); | ||
358 | 173 | if (settings.savegame) { // savegame | ||
359 | 174 | game->init_savegame(loader, settings); | ||
360 | 175 | } else { // new map | ||
361 | 176 | game->init_newgame(loader, settings); | ||
362 | 177 | } | ||
363 | 178 | return igb; | ||
364 | 179 | } | ||
365 | 180 | |||
366 | 181 | |||
367 | 182 | /** | ||
368 | 183 | * Run the actual game and cleanup when done. | ||
369 | 184 | */ | ||
370 | 185 | void GameClientImpl::run_game(InteractiveGameBase* igb, UI::ProgressWindow* loader) { | ||
371 | 186 | time.reset(game->get_gametime()); | ||
372 | 187 | lasttimestamp = game->get_gametime(); | ||
373 | 188 | lasttimestamp_realtime = SDL_GetTicks(); | ||
374 | 189 | |||
375 | 190 | modal = igb; | ||
376 | 191 | game->run(loader, settings.savegame ? | ||
377 | 192 | Widelands::Game::Loaded : | ||
378 | 193 | settings.scenario ? Widelands::Game::NewMPScenario : Widelands::Game::NewNonScenario, | ||
379 | 194 | "", false, (boost::format("netclient_%d") % static_cast<int>(settings.usernum)).str()); | ||
380 | 195 | |||
381 | 196 | // if this is an internet game, tell the metaserver that the game is done. | ||
382 | 197 | if (internet_) { | ||
383 | 198 | InternetGaming::ref().set_game_done(); | ||
384 | 199 | } | ||
385 | 200 | modal = nullptr; | ||
386 | 201 | game = nullptr; | ||
387 | 202 | } | ||
388 | 203 | |||
389 | 96 | GameClient::GameClient(const std::pair<NetAddress, NetAddress>& host, | 204 | GameClient::GameClient(const std::pair<NetAddress, NetAddress>& host, |
390 | 97 | const std::string& playername, | 205 | const std::string& playername, |
391 | 98 | bool internet, | 206 | bool internet, |
392 | 99 | const std::string& gamename) | 207 | const std::string& gamename) |
394 | 100 | : d(new GameClientImpl), internet_(internet) { | 208 | : d(new GameClientImpl) { |
395 | 209 | |||
396 | 210 | d->internet_ = internet; | ||
397 | 101 | 211 | ||
398 | 102 | if (internet) { | 212 | if (internet) { |
399 | 103 | assert(!gamename.empty()); | 213 | assert(!gamename.empty()); |
400 | @@ -127,7 +237,7 @@ | |||
401 | 127 | d->game = nullptr; | 237 | d->game = nullptr; |
402 | 128 | d->realspeed = 0; | 238 | d->realspeed = 0; |
403 | 129 | d->desiredspeed = 1000; | 239 | d->desiredspeed = 1000; |
405 | 130 | file_ = nullptr; | 240 | d->file_ = nullptr; |
406 | 131 | 241 | ||
407 | 132 | // Get the default win condition script | 242 | // Get the default win condition script |
408 | 133 | d->settings.win_condition_script = d->settings.win_condition_scripts.front(); | 243 | d->settings.win_condition_script = d->settings.win_condition_scripts.front(); |
409 | @@ -141,94 +251,41 @@ | |||
410 | 141 | delete d; | 251 | delete d; |
411 | 142 | } | 252 | } |
412 | 143 | 253 | ||
413 | 254 | |||
414 | 255 | |||
415 | 144 | void GameClient::run() { | 256 | void GameClient::run() { |
416 | 145 | SendPacket s; | ||
417 | 146 | s.unsigned_8(NETCMD_HELLO); | ||
418 | 147 | s.unsigned_8(NETWORK_PROTOCOL_VERSION); | ||
419 | 148 | s.string(d->localplayername); | ||
420 | 149 | s.string(build_id()); | ||
421 | 150 | d->net->send(s); | ||
422 | 151 | 257 | ||
423 | 258 | d->send_hello(); | ||
424 | 152 | d->settings.multiplayer = true; | 259 | d->settings.multiplayer = true; |
425 | 153 | 260 | ||
426 | 154 | // Fill the list of possible system messages | 261 | // Fill the list of possible system messages |
427 | 155 | NetworkGamingMessages::fill_map(); | 262 | NetworkGamingMessages::fill_map(); |
440 | 156 | { | 263 | |
441 | 157 | FullscreenMenuLaunchMPG lgm(this, this); | 264 | if (d->run_map_menu(this)) { |
442 | 158 | lgm.set_chat_provider(*this); | 265 | return; // did not select a Map ... |
431 | 159 | d->modal = &lgm; | ||
432 | 160 | FullscreenMenuBase::MenuTarget code = lgm.run<FullscreenMenuBase::MenuTarget>(); | ||
433 | 161 | d->modal = nullptr; | ||
434 | 162 | if (code == FullscreenMenuBase::MenuTarget::kBack) { | ||
435 | 163 | // if this is an internet game, tell the metaserver that client is back in the lobby. | ||
436 | 164 | if (internet_) | ||
437 | 165 | InternetGaming::ref().set_game_done(); | ||
438 | 166 | return; | ||
439 | 167 | } | ||
443 | 168 | } | 266 | } |
444 | 169 | 267 | ||
445 | 170 | d->server_is_waiting = true; | 268 | d->server_is_waiting = true; |
446 | 171 | 269 | ||
447 | 270 | bool write_sync_streams = g_options.pull_section("global").get_bool("write_syncstreams", true); | ||
448 | 172 | Widelands::Game game; | 271 | Widelands::Game game; |
450 | 173 | game.set_write_syncstream(g_options.pull_section("global").get_bool("write_syncstreams", true)); | 272 | game.set_write_syncstream(write_sync_streams); |
451 | 174 | 273 | ||
452 | 175 | try { | 274 | try { |
453 | 176 | std::unique_ptr<UI::ProgressWindow> loader_ui(new UI::ProgressWindow()); | 275 | std::unique_ptr<UI::ProgressWindow> loader_ui(new UI::ProgressWindow()); |
454 | 177 | d->modal = loader_ui.get(); | ||
455 | 178 | std::vector<std::string> tipstext; | ||
456 | 179 | tipstext.push_back("general_game"); | ||
457 | 180 | tipstext.push_back("multiplayer"); | ||
458 | 181 | try { | ||
459 | 182 | tipstext.push_back(get_players_tribe()); | ||
460 | 183 | } catch (NoTribe) { | ||
461 | 184 | } | ||
462 | 185 | GameTips tips(*loader_ui.get(), tipstext); | ||
463 | 186 | |||
464 | 187 | loader_ui->step(_("Preparing game")); | ||
465 | 188 | 276 | ||
466 | 189 | d->game = &game; | 277 | d->game = &game; |
500 | 190 | game.set_game_controller(this); | 278 | InteractiveGameBase* igb = d->init_game(this, loader_ui.get()); |
501 | 191 | uint8_t const pn = d->settings.playernum + 1; | 279 | d->run_game(igb, loader_ui.get()); |
502 | 192 | game.save_handler().set_autosave_filename( | 280 | |
470 | 193 | (boost::format("%s_netclient%u") % kAutosavePrefix % static_cast<unsigned int>(pn)).str()); | ||
471 | 194 | InteractiveGameBase* igb; | ||
472 | 195 | if (pn > 0) | ||
473 | 196 | igb = new InteractivePlayer(game, g_options.pull_section("global"), pn, true); | ||
474 | 197 | else | ||
475 | 198 | igb = new InteractiveSpectator(game, g_options.pull_section("global"), true); | ||
476 | 199 | game.set_ibase(igb); | ||
477 | 200 | igb->set_chat_provider(*this); | ||
478 | 201 | if (!d->settings.savegame) { // new map | ||
479 | 202 | game.init_newgame(loader_ui.get(), d->settings); | ||
480 | 203 | } else { // savegame | ||
481 | 204 | game.init_savegame(loader_ui.get(), d->settings); | ||
482 | 205 | } | ||
483 | 206 | d->time.reset(game.get_gametime()); | ||
484 | 207 | d->lasttimestamp = game.get_gametime(); | ||
485 | 208 | d->lasttimestamp_realtime = SDL_GetTicks(); | ||
486 | 209 | |||
487 | 210 | d->modal = igb; | ||
488 | 211 | game.run( | ||
489 | 212 | loader_ui.get(), | ||
490 | 213 | d->settings.savegame ? | ||
491 | 214 | Widelands::Game::Loaded : | ||
492 | 215 | d->settings.scenario ? Widelands::Game::NewMPScenario : Widelands::Game::NewNonScenario, | ||
493 | 216 | "", false, (boost::format("netclient_%d") % static_cast<int>(d->settings.usernum)).str()); | ||
494 | 217 | |||
495 | 218 | // if this is an internet game, tell the metaserver that the game is done. | ||
496 | 219 | if (internet_) | ||
497 | 220 | InternetGaming::ref().set_game_done(); | ||
498 | 221 | d->modal = nullptr; | ||
499 | 222 | d->game = nullptr; | ||
503 | 223 | } catch (...) { | 281 | } catch (...) { |
504 | 224 | WLApplication::emergency_save(game); | 282 | WLApplication::emergency_save(game); |
505 | 225 | d->game = nullptr; | 283 | d->game = nullptr; |
506 | 226 | disconnect("CLIENT_CRASHED"); | 284 | disconnect("CLIENT_CRASHED"); |
507 | 227 | // We will bounce back to the main menu, so we better log out | 285 | // We will bounce back to the main menu, so we better log out |
509 | 228 | if (internet_) { | 286 | if (d->internet_) { |
510 | 229 | InternetGaming::ref().logout("CLIENT_CRASHED"); | 287 | InternetGaming::ref().logout("CLIENT_CRASHED"); |
511 | 230 | } | 288 | } |
512 | 231 | d->modal = nullptr; | ||
513 | 232 | throw; | 289 | throw; |
514 | 233 | } | 290 | } |
515 | 234 | } | 291 | } |
516 | @@ -237,6 +294,7 @@ | |||
517 | 237 | handle_network(); | 294 | handle_network(); |
518 | 238 | 295 | ||
519 | 239 | if (d->game) { | 296 | if (d->game) { |
520 | 297 | // TODO(Klaus Halfmann): what kind of time tricks are done here? | ||
521 | 240 | if (d->realspeed == 0 || d->server_is_waiting) | 298 | if (d->realspeed == 0 || d->server_is_waiting) |
522 | 241 | d->time.fastforward(); | 299 | d->time.fastforward(); |
523 | 242 | else | 300 | else |
524 | @@ -254,25 +312,28 @@ | |||
525 | 254 | } | 312 | } |
526 | 255 | } | 313 | } |
527 | 256 | 314 | ||
529 | 257 | void GameClient::send_player_command(Widelands::PlayerCommand& pc) { | 315 | /** |
530 | 316 | * Send PlayerCommand to server. | ||
531 | 317 | * | ||
532 | 318 | * @param pc will always be deleted in the end. | ||
533 | 319 | */ | ||
534 | 320 | void GameClient::send_player_command(Widelands::PlayerCommand* pc) { | ||
535 | 258 | assert(d->game); | 321 | assert(d->game); |
539 | 259 | if (pc.sender() != d->settings.playernum + 1) { | 322 | |
540 | 260 | delete &pc; | 323 | // TODDO(Klaus Halfmann)should this be an assert? |
541 | 261 | return; | 324 | if (pc->sender() == d->settings.playernum + 1) // allow command for current player only |
542 | 325 | { | ||
543 | 326 | log("[Client]: send playercommand at time %i\n", d->game->get_gametime()); | ||
544 | 327 | |||
545 | 328 | d->send_player_command(pc); | ||
546 | 329 | |||
547 | 330 | d->lasttimestamp = d->game->get_gametime(); | ||
548 | 331 | d->lasttimestamp_realtime = SDL_GetTicks(); | ||
549 | 332 | } else { | ||
550 | 333 | log("[Client]: Playercommand is not for current player? %i\n", pc -> sender()); | ||
551 | 262 | } | 334 | } |
552 | 263 | 335 | ||
565 | 264 | log("[Client]: send playercommand at time %i\n", d->game->get_gametime()); | 336 | delete pc; |
554 | 265 | |||
555 | 266 | SendPacket s; | ||
556 | 267 | s.unsigned_8(NETCMD_PLAYERCOMMAND); | ||
557 | 268 | s.signed_32(d->game->get_gametime()); | ||
558 | 269 | pc.serialize(s); | ||
559 | 270 | d->net->send(s); | ||
560 | 271 | |||
561 | 272 | d->lasttimestamp = d->game->get_gametime(); | ||
562 | 273 | d->lasttimestamp_realtime = SDL_GetTicks(); | ||
563 | 274 | |||
564 | 275 | delete &pc; | ||
566 | 276 | } | 337 | } |
567 | 277 | 338 | ||
568 | 278 | int32_t GameClient::get_frametime() { | 339 | int32_t GameClient::get_frametime() { |
569 | @@ -544,6 +605,333 @@ | |||
570 | 544 | } | 605 | } |
571 | 545 | } | 606 | } |
572 | 546 | 607 | ||
573 | 608 | void GameClient::handle_disconnect(RecvPacket& packet) { | ||
574 | 609 | uint8_t number = packet.unsigned_8(); | ||
575 | 610 | std::string reason = packet.string(); | ||
576 | 611 | if (number == 1) | ||
577 | 612 | disconnect(reason, "", false); | ||
578 | 613 | else { | ||
579 | 614 | std::string arg = packet.string(); | ||
580 | 615 | disconnect(reason, arg, false); | ||
581 | 616 | } | ||
582 | 617 | } | ||
583 | 618 | |||
584 | 619 | /** | ||
585 | 620 | * Hello from the other side | ||
586 | 621 | */ | ||
587 | 622 | void GameClient::handle_hello(RecvPacket& packet) { | ||
588 | 623 | if (d->settings.usernum != -2) // TODO(Klaus Halfmann): if the host is the client ?. | ||
589 | 624 | throw ProtocolException(NETCMD_HELLO); // I am talkimg with myself? Bad idea | ||
590 | 625 | uint8_t const version = packet.unsigned_8(); | ||
591 | 626 | if (version != NETWORK_PROTOCOL_VERSION) | ||
592 | 627 | throw DisconnectException("DIFFERENT_PROTOCOL_VERS"); | ||
593 | 628 | d->settings.usernum = packet.unsigned_32(); // TODO(Klaus Halfmann): usernum is int8_t. | ||
594 | 629 | d->settings.playernum = -1; | ||
595 | 630 | } | ||
596 | 631 | |||
597 | 632 | /** | ||
598 | 633 | * Give a pong for a ping | ||
599 | 634 | */ | ||
600 | 635 | void GameClient::handle_ping(RecvPacket&) { | ||
601 | 636 | SendPacket s; | ||
602 | 637 | s.unsigned_8(NETCMD_PONG); | ||
603 | 638 | d->net->send(s); | ||
604 | 639 | |||
605 | 640 | log("[Client] Pong!\n"); | ||
606 | 641 | } | ||
607 | 642 | |||
608 | 643 | /** | ||
609 | 644 | * New Map name was sent. | ||
610 | 645 | */ | ||
611 | 646 | void GameClient::handle_setting_map(RecvPacket& packet) { | ||
612 | 647 | d->settings.mapname = packet.string(); | ||
613 | 648 | d->settings.mapfilename = g_fs->FileSystem::fix_cross_file(packet.string()); | ||
614 | 649 | d->settings.savegame = packet.unsigned_8() == 1; | ||
615 | 650 | d->settings.scenario = packet.unsigned_8() == 1; | ||
616 | 651 | log("[Client] SETTING_MAP '%s' '%s'\n", d->settings.mapname.c_str(), | ||
617 | 652 | d->settings.mapfilename.c_str()); | ||
618 | 653 | |||
619 | 654 | // New map was set, so we clean up the buffer of a previously requested file | ||
620 | 655 | d->file_.reset(nullptr); | ||
621 | 656 | } | ||
622 | 657 | |||
623 | 658 | /** | ||
624 | 659 | * | ||
625 | 660 | */ | ||
626 | 661 | // TODO(Klaus Halfmann): refactor this until it can be understood, move into impl. | ||
627 | 662 | void GameClient::handle_new_file(RecvPacket& packet) { | ||
628 | 663 | std::string path = g_fs->FileSystem::fix_cross_file(packet.string()); | ||
629 | 664 | uint32_t bytes = packet.unsigned_32(); | ||
630 | 665 | std::string md5 = packet.string(); | ||
631 | 666 | |||
632 | 667 | // Check whether the file or a file with that name already exists | ||
633 | 668 | if (g_fs->file_exists(path)) { | ||
634 | 669 | // If the file is a directory, we have to rename the file and replace it with the version | ||
635 | 670 | // of the host. If it is a zipped file, we can check, whether the host and the client have | ||
636 | 671 | // got the same file. | ||
637 | 672 | if (!g_fs->is_directory(path)) { | ||
638 | 673 | FileRead fr; | ||
639 | 674 | fr.open(*g_fs, path); | ||
640 | 675 | if (bytes == fr.get_size()) { | ||
641 | 676 | std::unique_ptr<char[]> complete(new char[bytes]); | ||
642 | 677 | if (!complete) { | ||
643 | 678 | throw wexception("Out of memory"); | ||
644 | 679 | } | ||
645 | 680 | fr.data_complete(complete.get(), bytes); | ||
646 | 681 | // TODO(Klaus Halfmann): compute MD5 on the fly in FileRead... | ||
647 | 682 | SimpleMD5Checksum md5sum; | ||
648 | 683 | md5sum.data(complete.get(), bytes); | ||
649 | 684 | md5sum.finish_checksum(); | ||
650 | 685 | std::string localmd5 = md5sum.get_checksum().str(); | ||
651 | 686 | if (localmd5 == md5) | ||
652 | 687 | // everything is alright we now have the file. | ||
653 | 688 | return; | ||
654 | 689 | } | ||
655 | 690 | } | ||
656 | 691 | // Don't overwrite the file, better rename the original one | ||
657 | 692 | try { | ||
658 | 693 | g_fs->fs_rename(path, backup_file_name(path)); | ||
659 | 694 | } catch (const FileError& e) { | ||
660 | 695 | log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " | ||
661 | 696 | "%s\n", | ||
662 | 697 | e.what()); | ||
663 | 698 | // TODO(Arty): What now? It just means the next step will fail | ||
664 | 699 | // or possibly result in some corrupt file | ||
665 | 700 | } | ||
666 | 701 | } | ||
667 | 702 | |||
668 | 703 | // Yes we need the file! | ||
669 | 704 | SendPacket s; | ||
670 | 705 | s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE); | ||
671 | 706 | d->net->send(s); | ||
672 | 707 | |||
673 | 708 | d->file_.reset(new NetTransferFile()); | ||
674 | 709 | d->file_->bytes = bytes; | ||
675 | 710 | d->file_->filename = path; | ||
676 | 711 | d->file_->md5sum = md5; | ||
677 | 712 | size_t position = path.rfind(g_fs->file_separator(), path.size() - 2); | ||
678 | 713 | if (position != std::string::npos) { | ||
679 | 714 | path.resize(position); | ||
680 | 715 | g_fs->ensure_directory_exists(path); | ||
681 | 716 | } | ||
682 | 717 | } | ||
683 | 718 | |||
684 | 719 | /** | ||
685 | 720 | * | ||
686 | 721 | */ | ||
687 | 722 | // TODO(Klaus Halfmann): refactor this until it can be understood, move into impl. | ||
688 | 723 | void GameClient::handle_file_part(RecvPacket& packet) { | ||
689 | 724 | // Only go on, if we are waiting for a file part at the moment. It can happen, that an | ||
690 | 725 | // "unrequested" part is send by the server if the map was changed just a moment ago | ||
691 | 726 | // and there was an outstanding request from the client. | ||
692 | 727 | if (!d->file_) | ||
693 | 728 | return; // silently ignore | ||
694 | 729 | |||
695 | 730 | uint32_t part = packet.unsigned_32(); | ||
696 | 731 | uint32_t size = packet.unsigned_32(); | ||
697 | 732 | |||
698 | 733 | // Send an answer | ||
699 | 734 | SendPacket s; | ||
700 | 735 | s.unsigned_8(NETCMD_FILE_PART); | ||
701 | 736 | s.unsigned_32(part); | ||
702 | 737 | s.string(d->file_->md5sum); | ||
703 | 738 | d->net->send(s); | ||
704 | 739 | |||
705 | 740 | FilePart fp; | ||
706 | 741 | |||
707 | 742 | char buf[NETFILEPARTSIZE]; | ||
708 | 743 | assert(size <= NETFILEPARTSIZE); | ||
709 | 744 | |||
710 | 745 | // TODO(Klaus Halfmann): read directcly into FilePart? | ||
711 | 746 | if (packet.data(buf, size) != size) | ||
712 | 747 | log("Readproblem. Will try to go on anyways\n"); | ||
713 | 748 | memcpy(fp.part, &buf[0], size); | ||
714 | 749 | d->file_->parts.push_back(fp); | ||
715 | 750 | |||
716 | 751 | // Write file to disk as soon as all parts arrived | ||
717 | 752 | uint32_t left = (d->file_->bytes - NETFILEPARTSIZE * part); | ||
718 | 753 | if (left <= NETFILEPARTSIZE) { | ||
719 | 754 | FileWrite fw; | ||
720 | 755 | left = d->file_->bytes; | ||
721 | 756 | uint32_t i = 0; | ||
722 | 757 | // Put all data together | ||
723 | 758 | while (left > 0) { | ||
724 | 759 | uint32_t writeout = (left > NETFILEPARTSIZE) ? NETFILEPARTSIZE : left; | ||
725 | 760 | fw.data(d->file_->parts[i].part, writeout, FileWrite::Pos::null()); | ||
726 | 761 | left -= writeout; | ||
727 | 762 | ++i; | ||
728 | 763 | } | ||
729 | 764 | // Now really write the file | ||
730 | 765 | fw.write(*g_fs, d->file_->filename.c_str()); | ||
731 | 766 | |||
732 | 767 | // Check for consistence | ||
733 | 768 | FileRead fr; | ||
734 | 769 | fr.open(*g_fs, d->file_->filename); | ||
735 | 770 | |||
736 | 771 | std::unique_ptr<char[]> complete(new char[d->file_->bytes]); | ||
737 | 772 | |||
738 | 773 | fr.data_complete(complete.get(), d->file_->bytes); | ||
739 | 774 | SimpleMD5Checksum md5sum; | ||
740 | 775 | md5sum.data(complete.get(), d->file_->bytes); | ||
741 | 776 | md5sum.finish_checksum(); | ||
742 | 777 | std::string localmd5 = md5sum.get_checksum().str(); | ||
743 | 778 | if (localmd5 != d->file_->md5sum) { | ||
744 | 779 | // Something went wrong! We have to rerequest the file. | ||
745 | 780 | s.reset(); | ||
746 | 781 | s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE); | ||
747 | 782 | d->net->send(s); | ||
748 | 783 | // Notify the players | ||
749 | 784 | s.reset(); | ||
750 | 785 | s.unsigned_8(NETCMD_CHAT); | ||
751 | 786 | s.string(_("/me 's file failed md5 checksumming.")); | ||
752 | 787 | d->net->send(s); | ||
753 | 788 | try { | ||
754 | 789 | g_fs->fs_unlink(d->file_->filename); | ||
755 | 790 | } catch (const FileError& e) { | ||
756 | 791 | log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " | ||
757 | 792 | "%s\n", | ||
758 | 793 | e.what()); | ||
759 | 794 | } | ||
760 | 795 | } | ||
761 | 796 | // Check file for validity | ||
762 | 797 | bool invalid = false; | ||
763 | 798 | if (d->settings.savegame) { | ||
764 | 799 | // Saved game check - does Widelands recognize the file as saved game? | ||
765 | 800 | Widelands::Game game; | ||
766 | 801 | try { | ||
767 | 802 | Widelands::GameLoader gl(d->file_->filename, game); | ||
768 | 803 | } catch (...) { | ||
769 | 804 | invalid = true; | ||
770 | 805 | } | ||
771 | 806 | } else { | ||
772 | 807 | // Map check - does Widelands recognize the file as map? | ||
773 | 808 | Widelands::Map map; | ||
774 | 809 | std::unique_ptr<Widelands::MapLoader> ml = map.get_correct_loader(d->file_->filename); | ||
775 | 810 | if (!ml) { | ||
776 | 811 | invalid = true; | ||
777 | 812 | } | ||
778 | 813 | } | ||
779 | 814 | if (invalid) { | ||
780 | 815 | try { | ||
781 | 816 | g_fs->fs_unlink(d->file_->filename); | ||
782 | 817 | // Restore original file, if there was one before | ||
783 | 818 | if (g_fs->file_exists(backup_file_name(d->file_->filename))) | ||
784 | 819 | g_fs->fs_rename(backup_file_name(d->file_->filename), d->file_->filename); | ||
785 | 820 | } catch (const FileError& e) { | ||
786 | 821 | log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " | ||
787 | 822 | "%s\n", e.what()); | ||
788 | 823 | } | ||
789 | 824 | s.reset(); | ||
790 | 825 | s.unsigned_8(NETCMD_CHAT); | ||
791 | 826 | s.string(_("/me checked the received file. Although md5 check summing succeeded, " | ||
792 | 827 | "I can not handle the file.")); | ||
793 | 828 | d->net->send(s); | ||
794 | 829 | } | ||
795 | 830 | } | ||
796 | 831 | } | ||
797 | 832 | |||
798 | 833 | /** | ||
799 | 834 | * | ||
800 | 835 | */ | ||
801 | 836 | void GameClient::handle_setting_tribes(RecvPacket& packet) { | ||
802 | 837 | d->settings.tribes.clear(); | ||
803 | 838 | for (uint8_t i = packet.unsigned_8(); i; --i) { | ||
804 | 839 | Widelands::TribeBasicInfo info = Widelands::get_tribeinfo(packet.string()); | ||
805 | 840 | |||
806 | 841 | // Get initializations (we have to do this locally, for translations) | ||
807 | 842 | LuaInterface lua; | ||
808 | 843 | info.initializations.clear(); | ||
809 | 844 | for (uint8_t j = packet.unsigned_8(); j > 0; --j) { | ||
810 | 845 | std::string const initialization_script = packet.string(); | ||
811 | 846 | std::unique_ptr<LuaTable> t = lua.run_script(initialization_script); | ||
812 | 847 | t->do_not_warn_about_unaccessed_keys(); | ||
813 | 848 | info.initializations.push_back(Widelands::TribeBasicInfo::Initialization( | ||
814 | 849 | initialization_script, t->get_string("descname"), t->get_string("tooltip"))); | ||
815 | 850 | } | ||
816 | 851 | d->settings.tribes.push_back(info); | ||
817 | 852 | } | ||
818 | 853 | } | ||
819 | 854 | |||
820 | 855 | /** | ||
821 | 856 | * | ||
822 | 857 | */ | ||
823 | 858 | void GameClient::handle_setting_allplayers(RecvPacket& packet) { | ||
824 | 859 | d->settings.players.resize(packet.unsigned_8()); | ||
825 | 860 | for (uint8_t i = 0; i < d->settings.players.size(); ++i) { | ||
826 | 861 | receive_one_player(i, packet); | ||
827 | 862 | } | ||
828 | 863 | // Map changes are finished here | ||
829 | 864 | Notifications::publish(NoteGameSettings(NoteGameSettings::Action::kMap)); | ||
830 | 865 | } | ||
831 | 866 | |||
832 | 867 | /** | ||
833 | 868 | * | ||
834 | 869 | */ | ||
835 | 870 | void GameClient::handle_playercommand(RecvPacket& packet) { | ||
836 | 871 | if (!d->game) | ||
837 | 872 | throw DisconnectException("PLAYERCMD_WO_GAME"); | ||
838 | 873 | |||
839 | 874 | int32_t const time = packet.signed_32(); | ||
840 | 875 | Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(packet); | ||
841 | 876 | plcmd.set_duetime(time); | ||
842 | 877 | d->game->enqueue_command(&plcmd); | ||
843 | 878 | d->time.receive(time); | ||
844 | 879 | } | ||
845 | 880 | |||
846 | 881 | /** | ||
847 | 882 | * | ||
848 | 883 | */ | ||
849 | 884 | void GameClient::handle_syncrequest(RecvPacket& packet) { | ||
850 | 885 | if (!d->game) | ||
851 | 886 | throw DisconnectException("SYNCREQUEST_WO_GAME"); | ||
852 | 887 | int32_t const time = packet.signed_32(); | ||
853 | 888 | d->time.receive(time); | ||
854 | 889 | d->game->enqueue_command(new CmdNetCheckSync(time, [this] { sync_report_callback(); })); | ||
855 | 890 | d->game->report_sync_request(); | ||
856 | 891 | } | ||
857 | 892 | |||
858 | 893 | /** | ||
859 | 894 | * | ||
860 | 895 | */ | ||
861 | 896 | void GameClient::handle_chat(RecvPacket& packet) { | ||
862 | 897 | ChatMessage c(""); | ||
863 | 898 | c.playern = packet.signed_16(); | ||
864 | 899 | c.sender = packet.string(); | ||
865 | 900 | c.msg = packet.string(); | ||
866 | 901 | if (packet.unsigned_8()) | ||
867 | 902 | c.recipient = packet.string(); | ||
868 | 903 | d->chatmessages.push_back(c); | ||
869 | 904 | Notifications::publish(c); | ||
870 | 905 | } | ||
871 | 906 | |||
872 | 907 | /** | ||
873 | 908 | * | ||
874 | 909 | */ | ||
875 | 910 | void GameClient::handle_system_message(RecvPacket& packet) { | ||
876 | 911 | const std::string code = packet.string(); | ||
877 | 912 | const std::string arg1 = packet.string(); | ||
878 | 913 | const std::string arg2 = packet.string(); | ||
879 | 914 | const std::string arg3 = packet.string(); | ||
880 | 915 | ChatMessage c(NetworkGamingMessages::get_message(code, arg1, arg2, arg3)); | ||
881 | 916 | c.playern = UserSettings::none(); // == System message | ||
882 | 917 | // c.sender remains empty to indicate a system message | ||
883 | 918 | d->chatmessages.push_back(c); | ||
884 | 919 | Notifications::publish(c); | ||
885 | 920 | } | ||
886 | 921 | |||
887 | 922 | /** | ||
888 | 923 | * | ||
889 | 924 | */ | ||
890 | 925 | void GameClient::handle_desync(RecvPacket&) { | ||
891 | 926 | log("[Client] received NETCMD_INFO_DESYNC. Trying to salvage some " | ||
892 | 927 | "information for debugging.\n"); | ||
893 | 928 | if (d->game) { | ||
894 | 929 | d->game->save_syncstream(true); | ||
895 | 930 | // We don't know our playernumber, so report as -1 | ||
896 | 931 | d->game->report_desync(-1); | ||
897 | 932 | } | ||
898 | 933 | } | ||
899 | 934 | |||
900 | 547 | /** | 935 | /** |
901 | 548 | * Handle one packet received from the host. | 936 | * Handle one packet received from the host. |
902 | 549 | * | 937 | * |
903 | @@ -552,363 +940,80 @@ | |||
904 | 552 | void GameClient::handle_packet(RecvPacket& packet) { | 940 | void GameClient::handle_packet(RecvPacket& packet) { |
905 | 553 | uint8_t cmd = packet.unsigned_8(); | 941 | uint8_t cmd = packet.unsigned_8(); |
906 | 554 | 942 | ||
921 | 555 | if (cmd == NETCMD_DISCONNECT) { | 943 | switch (cmd) { |
922 | 556 | uint8_t number = packet.unsigned_8(); | 944 | case NETCMD_DISCONNECT: |
923 | 557 | std::string reason = packet.string(); | 945 | return handle_disconnect(packet); |
924 | 558 | if (number == 1) | 946 | case NETCMD_HELLO: |
925 | 559 | disconnect(reason, "", false); | 947 | return handle_hello(packet); |
926 | 560 | else { | 948 | case NETCMD_PING: |
927 | 561 | std::string arg = packet.string(); | 949 | return handle_ping(packet); |
928 | 562 | disconnect(reason, arg, false); | 950 | case NETCMD_SETTING_MAP: |
929 | 563 | } | 951 | return handle_setting_map(packet); |
930 | 564 | return; | 952 | case NETCMD_NEW_FILE_AVAILABLE: |
931 | 565 | } | 953 | return handle_new_file(packet); |
932 | 566 | 954 | case NETCMD_FILE_PART: | |
933 | 567 | if (d->settings.usernum == -2) { | 955 | return handle_file_part(packet); |
934 | 568 | if (cmd != NETCMD_HELLO) | 956 | case NETCMD_SETTING_TRIBES: |
935 | 957 | return handle_setting_tribes(packet); | ||
936 | 958 | case NETCMD_SETTING_ALLPLAYERS: | ||
937 | 959 | return handle_setting_allplayers(packet); | ||
938 | 960 | case NETCMD_SETTING_PLAYER: { | ||
939 | 961 | uint8_t player = packet.unsigned_8(); | ||
940 | 962 | receive_one_player(player, packet); | ||
941 | 963 | } | ||
942 | 964 | break; | ||
943 | 965 | case NETCMD_SETTING_ALLUSERS: { | ||
944 | 966 | d->settings.users.resize(packet.unsigned_8()); | ||
945 | 967 | for (uint32_t i = 0; i < d->settings.users.size(); ++i) | ||
946 | 968 | receive_one_user(i, packet); | ||
947 | 969 | } | ||
948 | 970 | break; | ||
949 | 971 | case NETCMD_SETTING_USER: { | ||
950 | 972 | uint32_t user = packet.unsigned_32(); | ||
951 | 973 | receive_one_user(user, packet); | ||
952 | 974 | } | ||
953 | 975 | break; | ||
954 | 976 | case NETCMD_SET_PLAYERNUMBER: { | ||
955 | 977 | int32_t number = packet.signed_32(); | ||
956 | 978 | d->settings.playernum = number; | ||
957 | 979 | d->settings.users.at(d->settings.usernum).position = number; | ||
958 | 980 | } | ||
959 | 981 | break; | ||
960 | 982 | case NETCMD_WIN_CONDITION: | ||
961 | 983 | d->settings.win_condition_script = g_fs->FileSystem::fix_cross_file(packet.string()); | ||
962 | 984 | break; | ||
963 | 985 | case NETCMD_PEACEFUL_MODE: | ||
964 | 986 | d->settings.peaceful = packet.unsigned_8(); | ||
965 | 987 | break; | ||
966 | 988 | case NETCMD_LAUNCH: | ||
967 | 989 | if (!d->modal || d->game) { | ||
968 | 990 | throw DisconnectException("UNEXPECTED_LAUNCH"); | ||
969 | 991 | } | ||
970 | 992 | d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kOk); | ||
971 | 993 | break; | ||
972 | 994 | case NETCMD_SETSPEED: | ||
973 | 995 | d->realspeed = packet.unsigned_16(); | ||
974 | 996 | log("[Client] speed: %u.%03u\n", d->realspeed / 1000, d->realspeed % 1000); | ||
975 | 997 | break; | ||
976 | 998 | case NETCMD_TIME: | ||
977 | 999 | d->time.receive(packet.signed_32()); | ||
978 | 1000 | break; | ||
979 | 1001 | case NETCMD_WAIT: | ||
980 | 1002 | log("[Client]: server is waiting.\n"); | ||
981 | 1003 | d->server_is_waiting = true; | ||
982 | 1004 | break; | ||
983 | 1005 | case NETCMD_PLAYERCOMMAND: | ||
984 | 1006 | return handle_playercommand(packet); | ||
985 | 1007 | case NETCMD_SYNCREQUEST: | ||
986 | 1008 | return handle_syncrequest(packet); | ||
987 | 1009 | case NETCMD_CHAT: | ||
988 | 1010 | return handle_chat(packet); | ||
989 | 1011 | case NETCMD_SYSTEM_MESSAGE_CODE: | ||
990 | 1012 | return handle_system_message(packet); | ||
991 | 1013 | case NETCMD_INFO_DESYNC: | ||
992 | 1014 | return handle_desync(packet); | ||
993 | 1015 | default: | ||
994 | 569 | throw ProtocolException(cmd); | 1016 | throw ProtocolException(cmd); |
995 | 570 | uint8_t const version = packet.unsigned_8(); | ||
996 | 571 | if (version != NETWORK_PROTOCOL_VERSION) | ||
997 | 572 | throw DisconnectException("DIFFERENT_PROTOCOL_VERS"); | ||
998 | 573 | d->settings.usernum = packet.unsigned_32(); | ||
999 | 574 | d->settings.playernum = -1; | ||
1000 | 575 | return; | ||
1001 | 576 | } | ||
1002 | 577 | |||
1003 | 578 | switch (cmd) { | ||
1004 | 579 | case NETCMD_PING: { | ||
1005 | 580 | SendPacket s; | ||
1006 | 581 | s.unsigned_8(NETCMD_PONG); | ||
1007 | 582 | d->net->send(s); | ||
1008 | 583 | |||
1009 | 584 | log("[Client] Pong!\n"); | ||
1010 | 585 | break; | ||
1011 | 586 | } | ||
1012 | 587 | |||
1013 | 588 | case NETCMD_SETTING_MAP: { | ||
1014 | 589 | d->settings.mapname = packet.string(); | ||
1015 | 590 | d->settings.mapfilename = g_fs->FileSystem::fix_cross_file(packet.string()); | ||
1016 | 591 | d->settings.savegame = packet.unsigned_8() == 1; | ||
1017 | 592 | d->settings.scenario = packet.unsigned_8() == 1; | ||
1018 | 593 | log("[Client] SETTING_MAP '%s' '%s'\n", d->settings.mapname.c_str(), | ||
1019 | 594 | d->settings.mapfilename.c_str()); | ||
1020 | 595 | |||
1021 | 596 | // New map was set, so we clean up the buffer of a previously requested file | ||
1022 | 597 | file_.reset(nullptr); | ||
1023 | 598 | break; | ||
1024 | 599 | } | ||
1025 | 600 | |||
1026 | 601 | case NETCMD_NEW_FILE_AVAILABLE: { | ||
1027 | 602 | std::string path = g_fs->FileSystem::fix_cross_file(packet.string()); | ||
1028 | 603 | uint32_t bytes = packet.unsigned_32(); | ||
1029 | 604 | std::string md5 = packet.string(); | ||
1030 | 605 | |||
1031 | 606 | // Check whether the file or a file with that name already exists | ||
1032 | 607 | if (g_fs->file_exists(path)) { | ||
1033 | 608 | // If the file is a directory, we have to rename the file and replace it with the version | ||
1034 | 609 | // of the | ||
1035 | 610 | // host. If it is a ziped file, we can check, whether the host and the client have got the | ||
1036 | 611 | // same file. | ||
1037 | 612 | if (!g_fs->is_directory(path)) { | ||
1038 | 613 | FileRead fr; | ||
1039 | 614 | fr.open(*g_fs, path); | ||
1040 | 615 | if (bytes == fr.get_size()) { | ||
1041 | 616 | std::unique_ptr<char[]> complete(new char[bytes]); | ||
1042 | 617 | if (!complete) | ||
1043 | 618 | throw wexception("Out of memory"); | ||
1044 | 619 | |||
1045 | 620 | fr.data_complete(complete.get(), bytes); | ||
1046 | 621 | SimpleMD5Checksum md5sum; | ||
1047 | 622 | md5sum.data(complete.get(), bytes); | ||
1048 | 623 | md5sum.finish_checksum(); | ||
1049 | 624 | std::string localmd5 = md5sum.get_checksum().str(); | ||
1050 | 625 | if (localmd5 == md5) | ||
1051 | 626 | // everything is alright we already have the file. | ||
1052 | 627 | return; | ||
1053 | 628 | } | ||
1054 | 629 | } | ||
1055 | 630 | // Don't overwrite the file, better rename the original one | ||
1056 | 631 | try { | ||
1057 | 632 | g_fs->fs_rename(path, backup_file_name(path)); | ||
1058 | 633 | } catch (const FileError& e) { | ||
1059 | 634 | log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " | ||
1060 | 635 | "%s\n", | ||
1061 | 636 | e.what()); | ||
1062 | 637 | // TODO(Arty): What now? It just means the next step will fail | ||
1063 | 638 | // or possibly result in some corrupt file | ||
1064 | 639 | } | ||
1065 | 640 | } | ||
1066 | 641 | |||
1067 | 642 | // Yes we need the file! | ||
1068 | 643 | SendPacket s; | ||
1069 | 644 | s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE); | ||
1070 | 645 | d->net->send(s); | ||
1071 | 646 | |||
1072 | 647 | file_.reset(new NetTransferFile()); | ||
1073 | 648 | file_->bytes = bytes; | ||
1074 | 649 | file_->filename = path; | ||
1075 | 650 | file_->md5sum = md5; | ||
1076 | 651 | size_t position = path.rfind(g_fs->file_separator(), path.size() - 2); | ||
1077 | 652 | if (position != std::string::npos) { | ||
1078 | 653 | path.resize(position); | ||
1079 | 654 | g_fs->ensure_directory_exists(path); | ||
1080 | 655 | } | ||
1081 | 656 | break; | ||
1082 | 657 | } | ||
1083 | 658 | |||
1084 | 659 | case NETCMD_FILE_PART: { | ||
1085 | 660 | // Only go on, if we are waiting for a file part at the moment. It can happen, that an | ||
1086 | 661 | // "unrequested" | ||
1087 | 662 | // part is send by the server if the map was changed just a moment ago and there was an | ||
1088 | 663 | // outstanding | ||
1089 | 664 | // request from the client. | ||
1090 | 665 | if (!file_) | ||
1091 | 666 | return; // silently ignore | ||
1092 | 667 | |||
1093 | 668 | uint32_t part = packet.unsigned_32(); | ||
1094 | 669 | uint32_t size = packet.unsigned_32(); | ||
1095 | 670 | |||
1096 | 671 | // Send an answer | ||
1097 | 672 | SendPacket s; | ||
1098 | 673 | s.unsigned_8(NETCMD_FILE_PART); | ||
1099 | 674 | s.unsigned_32(part); | ||
1100 | 675 | s.string(file_->md5sum); | ||
1101 | 676 | d->net->send(s); | ||
1102 | 677 | |||
1103 | 678 | FilePart fp; | ||
1104 | 679 | |||
1105 | 680 | char buf[NETFILEPARTSIZE]; | ||
1106 | 681 | assert(size <= NETFILEPARTSIZE); | ||
1107 | 682 | |||
1108 | 683 | if (packet.data(buf, size) != size) | ||
1109 | 684 | log("Readproblem. Will try to go on anyways\n"); | ||
1110 | 685 | memcpy(fp.part, &buf[0], size); | ||
1111 | 686 | file_->parts.push_back(fp); | ||
1112 | 687 | |||
1113 | 688 | // Write file to disk as soon as all parts arrived | ||
1114 | 689 | uint32_t left = (file_->bytes - NETFILEPARTSIZE * part); | ||
1115 | 690 | if (left <= NETFILEPARTSIZE) { | ||
1116 | 691 | FileWrite fw; | ||
1117 | 692 | left = file_->bytes; | ||
1118 | 693 | uint32_t i = 0; | ||
1119 | 694 | // Put all data together | ||
1120 | 695 | while (left > 0) { | ||
1121 | 696 | uint32_t writeout = (left > NETFILEPARTSIZE) ? NETFILEPARTSIZE : left; | ||
1122 | 697 | fw.data(file_->parts[i].part, writeout, FileWrite::Pos::null()); | ||
1123 | 698 | left -= writeout; | ||
1124 | 699 | ++i; | ||
1125 | 700 | } | ||
1126 | 701 | // Now really write the file | ||
1127 | 702 | fw.write(*g_fs, file_->filename.c_str()); | ||
1128 | 703 | |||
1129 | 704 | // Check for consistence | ||
1130 | 705 | FileRead fr; | ||
1131 | 706 | fr.open(*g_fs, file_->filename); | ||
1132 | 707 | |||
1133 | 708 | std::unique_ptr<char[]> complete(new char[file_->bytes]); | ||
1134 | 709 | |||
1135 | 710 | fr.data_complete(complete.get(), file_->bytes); | ||
1136 | 711 | SimpleMD5Checksum md5sum; | ||
1137 | 712 | md5sum.data(complete.get(), file_->bytes); | ||
1138 | 713 | md5sum.finish_checksum(); | ||
1139 | 714 | std::string localmd5 = md5sum.get_checksum().str(); | ||
1140 | 715 | if (localmd5 != file_->md5sum) { | ||
1141 | 716 | // Something went wrong! We have to rerequest the file. | ||
1142 | 717 | s.reset(); | ||
1143 | 718 | s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE); | ||
1144 | 719 | d->net->send(s); | ||
1145 | 720 | // Notify the players | ||
1146 | 721 | s.reset(); | ||
1147 | 722 | s.unsigned_8(NETCMD_CHAT); | ||
1148 | 723 | s.string(_("/me 's file failed md5 checksumming.")); | ||
1149 | 724 | d->net->send(s); | ||
1150 | 725 | try { | ||
1151 | 726 | g_fs->fs_unlink(file_->filename); | ||
1152 | 727 | } catch (const FileError& e) { | ||
1153 | 728 | log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " | ||
1154 | 729 | "%s\n", | ||
1155 | 730 | e.what()); | ||
1156 | 731 | } | ||
1157 | 732 | } | ||
1158 | 733 | // Check file for validity | ||
1159 | 734 | bool invalid = false; | ||
1160 | 735 | if (d->settings.savegame) { | ||
1161 | 736 | // Saved game check - does Widelands recognize the file as saved game? | ||
1162 | 737 | Widelands::Game game; | ||
1163 | 738 | try { | ||
1164 | 739 | Widelands::GameLoader gl(file_->filename, game); | ||
1165 | 740 | } catch (...) { | ||
1166 | 741 | invalid = true; | ||
1167 | 742 | } | ||
1168 | 743 | } else { | ||
1169 | 744 | // Map check - does Widelands recognize the file as map? | ||
1170 | 745 | Widelands::Map map; | ||
1171 | 746 | std::unique_ptr<Widelands::MapLoader> ml = map.get_correct_loader(file_->filename); | ||
1172 | 747 | if (!ml) | ||
1173 | 748 | invalid = true; | ||
1174 | 749 | } | ||
1175 | 750 | if (invalid) { | ||
1176 | 751 | try { | ||
1177 | 752 | g_fs->fs_unlink(file_->filename); | ||
1178 | 753 | // Restore original file, if there was one before | ||
1179 | 754 | if (g_fs->file_exists(backup_file_name(file_->filename))) | ||
1180 | 755 | g_fs->fs_rename(backup_file_name(file_->filename), file_->filename); | ||
1181 | 756 | } catch (const FileError& e) { | ||
1182 | 757 | log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: " | ||
1183 | 758 | "%s\n", | ||
1184 | 759 | e.what()); | ||
1185 | 760 | } | ||
1186 | 761 | s.reset(); | ||
1187 | 762 | s.unsigned_8(NETCMD_CHAT); | ||
1188 | 763 | s.string(_("/me checked the received file. Although md5 check summing succeeded, " | ||
1189 | 764 | "I can not handle the file.")); | ||
1190 | 765 | d->net->send(s); | ||
1191 | 766 | } | ||
1192 | 767 | } | ||
1193 | 768 | break; | ||
1194 | 769 | } | ||
1195 | 770 | |||
1196 | 771 | case NETCMD_SETTING_TRIBES: { | ||
1197 | 772 | d->settings.tribes.clear(); | ||
1198 | 773 | for (uint8_t i = packet.unsigned_8(); i; --i) { | ||
1199 | 774 | Widelands::TribeBasicInfo info = Widelands::get_tribeinfo(packet.string()); | ||
1200 | 775 | |||
1201 | 776 | // Get initializations (we have to do this locally, for translations) | ||
1202 | 777 | LuaInterface lua; | ||
1203 | 778 | info.initializations.clear(); | ||
1204 | 779 | for (uint8_t j = packet.unsigned_8(); j; --j) { | ||
1205 | 780 | std::string const initialization_script = packet.string(); | ||
1206 | 781 | std::unique_ptr<LuaTable> t = lua.run_script(initialization_script); | ||
1207 | 782 | t->do_not_warn_about_unaccessed_keys(); | ||
1208 | 783 | info.initializations.push_back(Widelands::TribeBasicInfo::Initialization( | ||
1209 | 784 | initialization_script, t->get_string("descname"), t->get_string("tooltip"))); | ||
1210 | 785 | } | ||
1211 | 786 | d->settings.tribes.push_back(info); | ||
1212 | 787 | } | ||
1213 | 788 | break; | ||
1214 | 789 | } | ||
1215 | 790 | |||
1216 | 791 | case NETCMD_SETTING_ALLPLAYERS: { | ||
1217 | 792 | d->settings.players.resize(packet.unsigned_8()); | ||
1218 | 793 | for (uint8_t i = 0; i < d->settings.players.size(); ++i) { | ||
1219 | 794 | receive_one_player(i, packet); | ||
1220 | 795 | } | ||
1221 | 796 | // Map changes are finished here | ||
1222 | 797 | Notifications::publish(NoteGameSettings(NoteGameSettings::Action::kMap)); | ||
1223 | 798 | break; | ||
1224 | 799 | } | ||
1225 | 800 | case NETCMD_SETTING_PLAYER: { | ||
1226 | 801 | uint8_t player = packet.unsigned_8(); | ||
1227 | 802 | receive_one_player(player, packet); | ||
1228 | 803 | break; | ||
1229 | 804 | } | ||
1230 | 805 | case NETCMD_SETTING_ALLUSERS: { | ||
1231 | 806 | d->settings.users.resize(packet.unsigned_8()); | ||
1232 | 807 | for (uint32_t i = 0; i < d->settings.users.size(); ++i) | ||
1233 | 808 | receive_one_user(i, packet); | ||
1234 | 809 | break; | ||
1235 | 810 | } | ||
1236 | 811 | case NETCMD_SETTING_USER: { | ||
1237 | 812 | uint32_t user = packet.unsigned_32(); | ||
1238 | 813 | receive_one_user(user, packet); | ||
1239 | 814 | break; | ||
1240 | 815 | } | ||
1241 | 816 | case NETCMD_SET_PLAYERNUMBER: { | ||
1242 | 817 | int32_t number = packet.signed_32(); | ||
1243 | 818 | d->settings.playernum = number; | ||
1244 | 819 | d->settings.users.at(d->settings.usernum).position = number; | ||
1245 | 820 | break; | ||
1246 | 821 | } | ||
1247 | 822 | case NETCMD_WIN_CONDITION: { | ||
1248 | 823 | d->settings.win_condition_script = g_fs->FileSystem::fix_cross_file(packet.string()); | ||
1249 | 824 | break; | ||
1250 | 825 | } | ||
1251 | 826 | case NETCMD_PEACEFUL_MODE: { | ||
1252 | 827 | d->settings.peaceful = packet.unsigned_8(); | ||
1253 | 828 | break; | ||
1254 | 829 | } | ||
1255 | 830 | |||
1256 | 831 | case NETCMD_LAUNCH: { | ||
1257 | 832 | if (!d->modal || d->game) { | ||
1258 | 833 | throw DisconnectException("UNEXPECTED_LAUNCH"); | ||
1259 | 834 | } | ||
1260 | 835 | d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kOk); | ||
1261 | 836 | break; | ||
1262 | 837 | } | ||
1263 | 838 | |||
1264 | 839 | case NETCMD_SETSPEED: | ||
1265 | 840 | d->realspeed = packet.unsigned_16(); | ||
1266 | 841 | log("[Client] speed: %u.%03u\n", d->realspeed / 1000, d->realspeed % 1000); | ||
1267 | 842 | break; | ||
1268 | 843 | |||
1269 | 844 | case NETCMD_TIME: | ||
1270 | 845 | d->time.receive(packet.signed_32()); | ||
1271 | 846 | break; | ||
1272 | 847 | |||
1273 | 848 | case NETCMD_WAIT: | ||
1274 | 849 | log("[Client]: server is waiting.\n"); | ||
1275 | 850 | d->server_is_waiting = true; | ||
1276 | 851 | break; | ||
1277 | 852 | |||
1278 | 853 | case NETCMD_PLAYERCOMMAND: { | ||
1279 | 854 | if (!d->game) | ||
1280 | 855 | throw DisconnectException("PLAYERCMD_WO_GAME"); | ||
1281 | 856 | |||
1282 | 857 | int32_t const time = packet.signed_32(); | ||
1283 | 858 | Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(packet); | ||
1284 | 859 | plcmd.set_duetime(time); | ||
1285 | 860 | d->game->enqueue_command(&plcmd); | ||
1286 | 861 | d->time.receive(time); | ||
1287 | 862 | break; | ||
1288 | 863 | } | ||
1289 | 864 | |||
1290 | 865 | case NETCMD_SYNCREQUEST: { | ||
1291 | 866 | if (!d->game) | ||
1292 | 867 | throw DisconnectException("SYNCREQUEST_WO_GAME"); | ||
1293 | 868 | int32_t const time = packet.signed_32(); | ||
1294 | 869 | d->time.receive(time); | ||
1295 | 870 | d->game->enqueue_command(new CmdNetCheckSync(time, [this] { sync_report_callback(); })); | ||
1296 | 871 | d->game->report_sync_request(); | ||
1297 | 872 | break; | ||
1298 | 873 | } | ||
1299 | 874 | |||
1300 | 875 | case NETCMD_CHAT: { | ||
1301 | 876 | ChatMessage c(""); | ||
1302 | 877 | c.playern = packet.signed_16(); | ||
1303 | 878 | c.sender = packet.string(); | ||
1304 | 879 | c.msg = packet.string(); | ||
1305 | 880 | if (packet.unsigned_8()) | ||
1306 | 881 | c.recipient = packet.string(); | ||
1307 | 882 | d->chatmessages.push_back(c); | ||
1308 | 883 | Notifications::publish(c); | ||
1309 | 884 | break; | ||
1310 | 885 | } | ||
1311 | 886 | |||
1312 | 887 | case NETCMD_SYSTEM_MESSAGE_CODE: { | ||
1313 | 888 | const std::string code = packet.string(); | ||
1314 | 889 | const std::string arg1 = packet.string(); | ||
1315 | 890 | const std::string arg2 = packet.string(); | ||
1316 | 891 | const std::string arg3 = packet.string(); | ||
1317 | 892 | ChatMessage c(NetworkGamingMessages::get_message(code, arg1, arg2, arg3)); | ||
1318 | 893 | c.playern = UserSettings::none(); // == System message | ||
1319 | 894 | // c.sender remains empty to indicate a system message | ||
1320 | 895 | d->chatmessages.push_back(c); | ||
1321 | 896 | Notifications::publish(c); | ||
1322 | 897 | break; | ||
1323 | 898 | } | ||
1324 | 899 | |||
1325 | 900 | case NETCMD_INFO_DESYNC: | ||
1326 | 901 | log("[Client] received NETCMD_INFO_DESYNC. Trying to salvage some " | ||
1327 | 902 | "information for debugging.\n"); | ||
1328 | 903 | if (d->game) { | ||
1329 | 904 | d->game->save_syncstream(true); | ||
1330 | 905 | // We don't know our playernumber, so report as -1 | ||
1331 | 906 | d->game->report_desync(-1); | ||
1332 | 907 | } | ||
1333 | 908 | break; | ||
1334 | 909 | |||
1335 | 910 | default: | ||
1336 | 911 | throw ProtocolException(cmd); | ||
1337 | 912 | } | 1017 | } |
1338 | 913 | } | 1018 | } |
1339 | 914 | 1019 | ||
1340 | @@ -917,7 +1022,7 @@ | |||
1341 | 917 | */ | 1022 | */ |
1342 | 918 | void GameClient::handle_network() { | 1023 | void GameClient::handle_network() { |
1343 | 919 | // if this is an internet game, handle the metaserver network | 1024 | // if this is an internet game, handle the metaserver network |
1345 | 920 | if (internet_) | 1025 | if (d->internet_) |
1346 | 921 | InternetGaming::ref().handle_metaserver_communication(); | 1026 | InternetGaming::ref().handle_metaserver_communication(); |
1347 | 922 | try { | 1027 | try { |
1348 | 923 | assert(d->net != nullptr); | 1028 | assert(d->net != nullptr); |
1349 | @@ -965,7 +1070,7 @@ | |||
1350 | 965 | 1070 | ||
1351 | 966 | bool const trysave = showmsg && d->game; | 1071 | bool const trysave = showmsg && d->game; |
1352 | 967 | 1072 | ||
1354 | 968 | if (showmsg) { | 1073 | if (showmsg && d->modal) { // can only show a message with a valid modal parent window |
1355 | 969 | std::string msg; | 1074 | std::string msg; |
1356 | 970 | if (arg.empty()) | 1075 | if (arg.empty()) |
1357 | 971 | msg = NetworkGamingMessages::get_message(reason); | 1076 | msg = NetworkGamingMessages::get_message(reason); |
1358 | @@ -985,8 +1090,9 @@ | |||
1359 | 985 | if (trysave) | 1090 | if (trysave) |
1360 | 986 | WLApplication::emergency_save(*d->game); | 1091 | WLApplication::emergency_save(*d->game); |
1361 | 987 | 1092 | ||
1362 | 1093 | // TODO(Klaus Halfmann): Some of the modal windows are now handled by unique_ptr resulting in a double free. | ||
1363 | 988 | if (d->modal) { | 1094 | if (d->modal) { |
1364 | 989 | d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kBack); | 1095 | d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kBack); |
1365 | 990 | d->modal = nullptr; | ||
1366 | 991 | } | 1096 | } |
1367 | 1097 | d->modal = nullptr; | ||
1368 | 992 | } | 1098 | } |
1369 | 993 | 1099 | ||
1370 | === modified file 'src/network/gameclient.h' | |||
1371 | --- src/network/gameclient.h 2019-04-29 16:22:08 +0000 | |||
1372 | +++ src/network/gameclient.h 2019-05-25 17:10:52 +0000 | |||
1373 | @@ -29,14 +29,16 @@ | |||
1374 | 29 | #include "network/netclient_interface.h" | 29 | #include "network/netclient_interface.h" |
1375 | 30 | 30 | ||
1376 | 31 | struct GameClientImpl; | 31 | struct GameClientImpl; |
1377 | 32 | class InteractiveGameBase; | ||
1378 | 32 | 33 | ||
1379 | 33 | // TODO(unknown): Use composition instead of inheritance | ||
1380 | 34 | /** | 34 | /** |
1381 | 35 | * GameClient manages the lifetime of a network game in which this computer | 35 | * GameClient manages the lifetime of a network game in which this computer |
1382 | 36 | * participates as a client. | 36 | * participates as a client. |
1383 | 37 | * | 37 | * |
1384 | 38 | * This includes running the game setup screen and the actual game after | 38 | * This includes running the game setup screen and the actual game after |
1385 | 39 | * launch, as well as dealing with the actual network protocol. | 39 | * launch, as well as dealing with the actual network protocol. |
1386 | 40 | * | ||
1387 | 41 | * @param internet TODO(Klaus Halfmann): true: coonnect into the open internet via proxy, false connect locally / via IP. | ||
1388 | 40 | */ | 42 | */ |
1389 | 41 | struct GameClient : public GameController, public GameSettingsProvider, public ChatProvider { | 43 | struct GameClient : public GameController, public GameSettingsProvider, public ChatProvider { |
1390 | 42 | GameClient(const std::pair<NetAddress, NetAddress>& host, | 44 | GameClient(const std::pair<NetAddress, NetAddress>& host, |
1391 | @@ -50,7 +52,7 @@ | |||
1392 | 50 | 52 | ||
1393 | 51 | // GameController interface | 53 | // GameController interface |
1394 | 52 | void think() override; | 54 | void think() override; |
1396 | 53 | void send_player_command(Widelands::PlayerCommand&) override; | 55 | void send_player_command(Widelands::PlayerCommand*) override; |
1397 | 54 | int32_t get_frametime() override; | 56 | int32_t get_frametime() override; |
1398 | 55 | GameController::GameType get_game_type() override; | 57 | GameController::GameType get_game_type() override; |
1399 | 56 | 58 | ||
1400 | @@ -115,7 +117,21 @@ | |||
1401 | 115 | 117 | ||
1402 | 116 | void sync_report_callback(); | 118 | void sync_report_callback(); |
1403 | 117 | 119 | ||
1405 | 118 | void handle_packet(RecvPacket&); | 120 | void handle_hello(RecvPacket& packet); |
1406 | 121 | void handle_disconnect(RecvPacket& packet); | ||
1407 | 122 | void handle_ping(RecvPacket& packet); | ||
1408 | 123 | void handle_new_file(RecvPacket& packet); | ||
1409 | 124 | void handle_syncrequest(RecvPacket& packet); | ||
1410 | 125 | void handle_setting_map(RecvPacket& packet); | ||
1411 | 126 | void handle_file_part(RecvPacket& packet); | ||
1412 | 127 | void handle_setting_tribes(RecvPacket& packet); | ||
1413 | 128 | void handle_setting_allplayers(RecvPacket& packet); | ||
1414 | 129 | void handle_playercommand(RecvPacket& packet); | ||
1415 | 130 | void handle_chat(RecvPacket& packet); | ||
1416 | 131 | void handle_system_message(RecvPacket& packet); | ||
1417 | 132 | void handle_desync(RecvPacket& packet); | ||
1418 | 133 | void handle_packet(RecvPacket& packet); | ||
1419 | 134 | |||
1420 | 119 | void handle_network(); | 135 | void handle_network(); |
1421 | 120 | void send_time(); | 136 | void send_time(); |
1422 | 121 | void receive_one_player(uint8_t number, StreamRead&); | 137 | void receive_one_player(uint8_t number, StreamRead&); |
1423 | @@ -125,9 +141,9 @@ | |||
1424 | 125 | bool sendreason = true, | 141 | bool sendreason = true, |
1425 | 126 | bool showmsg = true); | 142 | bool showmsg = true); |
1426 | 127 | 143 | ||
1428 | 128 | std::unique_ptr<NetTransferFile> file_; | 144 | |
1429 | 129 | GameClientImpl* d; | 145 | GameClientImpl* d; |
1431 | 130 | bool internet_; | 146 | |
1432 | 131 | }; | 147 | }; |
1433 | 132 | 148 | ||
1434 | 133 | #endif // end of include guard: WL_NETWORK_GAMECLIENT_H | 149 | #endif // end of include guard: WL_NETWORK_GAMECLIENT_H |
1435 | 134 | 150 | ||
1436 | === modified file 'src/network/gamehost.cc' | |||
1437 | --- src/network/gamehost.cc 2019-05-11 22:55:40 +0000 | |||
1438 | +++ src/network/gamehost.cc 2019-05-25 17:10:52 +0000 | |||
1439 | @@ -761,15 +761,15 @@ | |||
1440 | 761 | } | 761 | } |
1441 | 762 | } | 762 | } |
1442 | 763 | 763 | ||
1445 | 764 | void GameHost::send_player_command(Widelands::PlayerCommand& pc) { | 764 | void GameHost::send_player_command(Widelands::PlayerCommand* pc) { |
1446 | 765 | pc.set_duetime(d->committed_networktime + 1); | 765 | pc->set_duetime(d->committed_networktime + 1); |
1447 | 766 | 766 | ||
1448 | 767 | SendPacket packet; | 767 | SendPacket packet; |
1449 | 768 | packet.unsigned_8(NETCMD_PLAYERCOMMAND); | 768 | packet.unsigned_8(NETCMD_PLAYERCOMMAND); |
1452 | 769 | packet.signed_32(pc.duetime()); | 769 | packet.signed_32(pc->duetime()); |
1453 | 770 | pc.serialize(packet); | 770 | pc-> serialize(packet); |
1454 | 771 | broadcast(packet); | 771 | broadcast(packet); |
1456 | 772 | d->game->enqueue_command(&pc); | 772 | d->game->enqueue_command(pc); |
1457 | 773 | 773 | ||
1458 | 774 | committed_network_time(d->committed_networktime + 1); | 774 | committed_network_time(d->committed_networktime + 1); |
1459 | 775 | } | 775 | } |
1460 | @@ -2181,11 +2181,11 @@ | |||
1461 | 2181 | if (!d->game) | 2181 | if (!d->game) |
1462 | 2182 | throw DisconnectException("PLAYERCMD_WO_GAME"); | 2182 | throw DisconnectException("PLAYERCMD_WO_GAME"); |
1463 | 2183 | int32_t time = r.signed_32(); | 2183 | int32_t time = r.signed_32(); |
1465 | 2184 | Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(r); | 2184 | Widelands::PlayerCommand* plcmd = Widelands::PlayerCommand::deserialize(r); |
1466 | 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, |
1468 | 2186 | static_cast<unsigned int>(plcmd.id()), plcmd.sender(), time); | 2186 | static_cast<unsigned int>(plcmd->id()), plcmd->sender(), time); |
1469 | 2187 | receive_client_time(i, time); | 2187 | receive_client_time(i, time); |
1471 | 2188 | if (plcmd.sender() != client.playernum + 1) | 2188 | if (plcmd->sender() != client.playernum + 1) |
1472 | 2189 | throw DisconnectException("PLAYERCMD_FOR_OTHER"); | 2189 | throw DisconnectException("PLAYERCMD_FOR_OTHER"); |
1473 | 2190 | send_player_command(plcmd); | 2190 | send_player_command(plcmd); |
1474 | 2191 | } break; | 2191 | } break; |
1475 | 2192 | 2192 | ||
1476 | === modified file 'src/network/gamehost.h' | |||
1477 | --- src/network/gamehost.h 2019-05-04 10:47:44 +0000 | |||
1478 | +++ src/network/gamehost.h 2019-05-25 17:10:52 +0000 | |||
1479 | @@ -50,7 +50,7 @@ | |||
1480 | 50 | 50 | ||
1481 | 51 | // GameController interface | 51 | // GameController interface |
1482 | 52 | void think() override; | 52 | void think() override; |
1484 | 53 | void send_player_command(Widelands::PlayerCommand&) override; | 53 | void send_player_command(Widelands::PlayerCommand*) override; |
1485 | 54 | int32_t get_frametime() override; | 54 | int32_t get_frametime() override; |
1486 | 55 | GameController::GameType get_game_type() override; | 55 | GameController::GameType get_game_type() override; |
1487 | 56 | 56 | ||
1488 | 57 | 57 | ||
1489 | === modified file 'src/network/netclient_interface.h' | |||
1490 | --- src/network/netclient_interface.h 2019-02-23 11:00:49 +0000 | |||
1491 | +++ src/network/netclient_interface.h 2019-05-25 17:10:52 +0000 | |||
1492 | @@ -27,6 +27,7 @@ | |||
1493 | 27 | /** | 27 | /** |
1494 | 28 | * NetClient manages the network connection for a network game in which this computer | 28 | * NetClient manages the network connection for a network game in which this computer |
1495 | 29 | * participates as a client. | 29 | * participates as a client. |
1496 | 30 | * | ||
1497 | 30 | * This class provides the interface all NetClient implementation have to follow. | 31 | * This class provides the interface all NetClient implementation have to follow. |
1498 | 31 | * Currently two implementations exists: A "real" NetClient for local games and a | 32 | * Currently two implementations exists: A "real" NetClient for local games and a |
1499 | 32 | * NetClientProxy which relays commands over a relay server. | 33 | * NetClientProxy which relays commands over a relay server. |
1500 | @@ -42,18 +43,21 @@ | |||
1501 | 42 | 43 | ||
1502 | 43 | /** | 44 | /** |
1503 | 44 | * Returns whether the client is connected. | 45 | * Returns whether the client is connected. |
1504 | 46 | * | ||
1505 | 45 | * \return \c true if the connection is open, \c false otherwise. | 47 | * \return \c true if the connection is open, \c false otherwise. |
1506 | 46 | */ | 48 | */ |
1507 | 47 | virtual bool is_connected() const = 0; | 49 | virtual bool is_connected() const = 0; |
1508 | 48 | 50 | ||
1509 | 49 | /** | 51 | /** |
1510 | 50 | * Closes the connection. | 52 | * Closes the connection. |
1511 | 53 | * | ||
1512 | 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. |
1513 | 52 | */ | 55 | */ |
1514 | 53 | virtual void close() = 0; | 56 | virtual void close() = 0; |
1515 | 54 | 57 | ||
1516 | 55 | /** | 58 | /** |
1517 | 56 | * Tries to receive a packet. | 59 | * Tries to receive a packet. |
1518 | 60 | * | ||
1519 | 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. |
1520 | 58 | * Calling this on a closed connection will return an invalid pointer. | 62 | * Calling this on a closed connection will return an invalid pointer. |
1521 | 59 | */ | 63 | */ |
1522 | @@ -61,6 +65,7 @@ | |||
1523 | 61 | 65 | ||
1524 | 62 | /** | 66 | /** |
1525 | 63 | * Sends a packet. | 67 | * Sends a packet. |
1526 | 68 | * | ||
1527 | 64 | * Calling this on a closed connection will silently fail. | 69 | * Calling this on a closed connection will silently fail. |
1528 | 65 | * \param packet The packet to send. | 70 | * \param packet The packet to send. |
1529 | 66 | */ | 71 | */ |
1530 | 67 | 72 | ||
1531 | === modified file 'src/network/nethostproxy.cc' | |||
1532 | --- src/network/nethostproxy.cc 2018-03-03 10:48:14 +0000 | |||
1533 | +++ src/network/nethostproxy.cc 2019-05-25 17:10:52 +0000 | |||
1534 | @@ -256,7 +256,7 @@ | |||
1535 | 256 | conn_->receive(&cmd); | 256 | conn_->receive(&cmd); |
1536 | 257 | uint8_t id; | 257 | uint8_t id; |
1537 | 258 | conn_->receive(&id); | 258 | conn_->receive(&id); |
1539 | 259 | assert(clients_.count(id)); | 259 | assert(clients_.count(id)); // TODO(Klaus Halfmann): As of a race condition this may not always hold. |
1540 | 260 | clients_.at(id).state_ = Client::State::kDisconnected; | 260 | clients_.at(id).state_ = Client::State::kDisconnected; |
1541 | 261 | } | 261 | } |
1542 | 262 | break; | 262 | break; |
1543 | 263 | 263 | ||
1544 | === modified file 'src/wui/economy_options_window.cc' | |||
1545 | --- src/wui/economy_options_window.cc 2019-02-23 11:00:49 +0000 | |||
1546 | +++ src/wui/economy_options_window.cc 2019-05-25 17:10:52 +0000 | |||
1547 | @@ -189,11 +189,11 @@ | |||
1548 | 189 | // Don't allow negative new amount. | 189 | // Don't allow negative new amount. |
1549 | 190 | if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) { | 190 | if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) { |
1550 | 191 | if (is_wares) { | 191 | if (is_wares) { |
1552 | 192 | game.send_player_command(*new Widelands::CmdSetWareTargetQuantity( | 192 | game.send_player_command(new Widelands::CmdSetWareTargetQuantity( |
1553 | 193 | game.get_gametime(), player_->player_number(), serial_, index, | 193 | game.get_gametime(), player_->player_number(), serial_, index, |
1554 | 194 | tq.permanent + amount)); | 194 | tq.permanent + amount)); |
1555 | 195 | } else { | 195 | } else { |
1557 | 196 | game.send_player_command(*new Widelands::CmdSetWorkerTargetQuantity( | 196 | game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity( |
1558 | 197 | game.get_gametime(), player_->player_number(), serial_, index, | 197 | game.get_gametime(), player_->player_number(), serial_, index, |
1559 | 198 | tq.permanent + amount)); | 198 | tq.permanent + amount)); |
1560 | 199 | } | 199 | } |
1561 | @@ -209,10 +209,10 @@ | |||
1562 | 209 | for (const Widelands::DescriptionIndex& index : items) { | 209 | for (const Widelands::DescriptionIndex& index : items) { |
1563 | 210 | if (display_.ware_selected(index)) { | 210 | if (display_.ware_selected(index)) { |
1564 | 211 | if (is_wares) { | 211 | if (is_wares) { |
1566 | 212 | game.send_player_command(*new Widelands::CmdResetWareTargetQuantity( | 212 | game.send_player_command(new Widelands::CmdResetWareTargetQuantity( |
1567 | 213 | game.get_gametime(), player_->player_number(), serial_, index)); | 213 | game.get_gametime(), player_->player_number(), serial_, index)); |
1568 | 214 | } else { | 214 | } else { |
1570 | 215 | game.send_player_command(*new Widelands::CmdResetWorkerTargetQuantity( | 215 | game.send_player_command(new Widelands::CmdResetWorkerTargetQuantity( |
1571 | 216 | game.get_gametime(), player_->player_number(), serial_, index)); | 216 | game.get_gametime(), player_->player_number(), serial_, index)); |
1572 | 217 | } | 217 | } |
1573 | 218 | } | 218 | } |
1574 | 219 | 219 | ||
1575 | === modified file 'src/wui/game_message_menu.cc' | |||
1576 | --- src/wui/game_message_menu.cc 2019-04-18 16:50:35 +0000 | |||
1577 | +++ src/wui/game_message_menu.cc 2019-05-25 17:10:52 +0000 | |||
1578 | @@ -336,7 +336,7 @@ | |||
1579 | 336 | // Maybe the message was removed since think? | 336 | // Maybe the message was removed since think? |
1580 | 337 | if (message->status() == Message::Status::kNew) { | 337 | if (message->status() == Message::Status::kNew) { |
1581 | 338 | Widelands::Game& game = iplayer().game(); | 338 | Widelands::Game& game = iplayer().game(); |
1583 | 339 | game.send_player_command(*new Widelands::CmdMessageSetStatusRead( | 339 | game.send_player_command(new Widelands::CmdMessageSetStatusRead( |
1584 | 340 | game.get_gametime(), player.player_number(), id)); | 340 | game.get_gametime(), player.player_number(), id)); |
1585 | 341 | } | 341 | } |
1586 | 342 | centerviewbtn_->set_enabled(message->position()); | 342 | centerviewbtn_->set_enabled(message->position()); |
1587 | @@ -439,12 +439,12 @@ | |||
1588 | 439 | switch (mode) { | 439 | switch (mode) { |
1589 | 440 | case Inbox: | 440 | case Inbox: |
1590 | 441 | // Archive highlighted message | 441 | // Archive highlighted message |
1592 | 442 | game.send_player_command(*new Widelands::CmdMessageSetStatusArchived( | 442 | game.send_player_command(new Widelands::CmdMessageSetStatusArchived( |
1593 | 443 | game.get_gametime(), plnum, MessageId(selected_record))); | 443 | game.get_gametime(), plnum, MessageId(selected_record))); |
1594 | 444 | break; | 444 | break; |
1595 | 445 | case Archive: | 445 | case Archive: |
1596 | 446 | // Restore highlighted message | 446 | // Restore highlighted message |
1598 | 447 | game.send_player_command(*new Widelands::CmdMessageSetStatusRead( | 447 | game.send_player_command(new Widelands::CmdMessageSetStatusRead( |
1599 | 448 | game.get_gametime(), plnum, MessageId(selected_record))); | 448 | game.get_gametime(), plnum, MessageId(selected_record))); |
1600 | 449 | break; | 449 | break; |
1601 | 450 | } | 450 | } |
1602 | 451 | 451 | ||
1603 | === modified file 'src/wui/warehousewindow.cc' | |||
1604 | --- src/wui/warehousewindow.cc 2019-02-23 11:00:49 +0000 | |||
1605 | +++ src/wui/warehousewindow.cc 2019-05-25 17:10:52 +0000 | |||
1606 | @@ -160,7 +160,7 @@ | |||
1607 | 160 | 160 | ||
1608 | 161 | for (const Widelands::DescriptionIndex& index : indices) { | 161 | for (const Widelands::DescriptionIndex& index : indices) { |
1609 | 162 | if (display_.ware_selected(index)) { | 162 | if (display_.ware_selected(index)) { |
1611 | 163 | gb_.game().send_player_command(*new Widelands::CmdSetStockPolicy( | 163 | gb_.game().send_player_command(new Widelands::CmdSetStockPolicy( |
1612 | 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, |
1613 | 165 | newpolicy)); | 165 | newpolicy)); |
1614 | 166 | } | 166 | } |
Regressiontest are ok: Ran 44 tests in 1377.106s
Will try to do a network game, too.