← Back to team overview

widelands-dev team mailing list archive

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

 

Klaus Halfmann has proposed merging lp:~widelands-dev/widelands/refactor_gameclient into lp:widelands.

Commit message:
Refactor gameclient.h/.cc for better readability



Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/refactor_gameclient/+merge/366743

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

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

There should be no functional change at all.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/refactor_gameclient into lp:widelands.
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc	2019-04-10 16:39:31 +0000
+++ src/ai/defaultai.cc	2019-05-01 07:38:30 +0000
@@ -6544,7 +6544,7 @@
 			const uint16_t new_target = std::max<uint16_t>(default_target * multiplier / 10, 3);
 			assert(new_target > 1);
 
-			game().send_player_command(*new Widelands::CmdSetWareTargetQuantity(
+			game().send_player_command(new Widelands::CmdSetWareTargetQuantity(
 			   gametime, player_number(), observer->economy.serial(), id, new_target));
 		}
 	}

=== modified file 'src/logic/game.cc'
--- src/logic/game.cc	2019-04-26 16:52:39 +0000
+++ src/logic/game.cc	2019-05-01 07:38:30 +0000
@@ -712,7 +712,7 @@
  * It takes the appropriate action, i.e. either add to the cmd_queue or send
  * across the network.
  */
-void Game::send_player_command(PlayerCommand& pc) {
+void Game::send_player_command(PlayerCommand* pc) {
 	ctrl_->send_player_command(pc);
 }
 
@@ -735,55 +735,55 @@
 
 // we might want to make these inlines:
 void Game::send_player_bulldoze(PlayerImmovable& pi, bool const recurse) {
-	send_player_command(*new CmdBulldoze(get_gametime(), pi.owner().player_number(), pi, recurse));
+	send_player_command(new CmdBulldoze(get_gametime(), pi.owner().player_number(), pi, recurse));
 }
 
 void Game::send_player_dismantle(PlayerImmovable& pi) {
-	send_player_command(*new CmdDismantleBuilding(get_gametime(), pi.owner().player_number(), pi));
+	send_player_command(new CmdDismantleBuilding(get_gametime(), pi.owner().player_number(), pi));
 }
 
 void Game::send_player_build(int32_t const pid, const Coords& coords, DescriptionIndex const id) {
 	assert(tribes().building_exists(id));
-	send_player_command(*new CmdBuild(get_gametime(), pid, coords, id));
+	send_player_command(new CmdBuild(get_gametime(), pid, coords, id));
 }
 
 void Game::send_player_build_flag(int32_t const pid, const Coords& coords) {
-	send_player_command(*new CmdBuildFlag(get_gametime(), pid, coords));
+	send_player_command(new CmdBuildFlag(get_gametime(), pid, coords));
 }
 
 void Game::send_player_build_road(int32_t pid, Path& path) {
-	send_player_command(*new CmdBuildRoad(get_gametime(), pid, path));
+	send_player_command(new CmdBuildRoad(get_gametime(), pid, path));
 }
 
 void Game::send_player_flagaction(Flag& flag) {
-	send_player_command(*new CmdFlagAction(get_gametime(), flag.owner().player_number(), flag));
+	send_player_command(new CmdFlagAction(get_gametime(), flag.owner().player_number(), flag));
 }
 
 void Game::send_player_start_stop_building(Building& building) {
 	send_player_command(
-	   *new CmdStartStopBuilding(get_gametime(), building.owner().player_number(), building));
+	   new CmdStartStopBuilding(get_gametime(), building.owner().player_number(), building));
 }
 
 void Game::send_player_militarysite_set_soldier_preference(Building& building,
                                                            SoldierPreference my_preference) {
-	send_player_command(*new CmdMilitarySiteSetSoldierPreference(
+	send_player_command(new CmdMilitarySiteSetSoldierPreference(
 	   get_gametime(), building.owner().player_number(), building, my_preference));
 }
 
 void Game::send_player_start_or_cancel_expedition(Building& building) {
 	send_player_command(
-	   *new CmdStartOrCancelExpedition(get_gametime(), building.owner().player_number(), building));
+	   new CmdStartOrCancelExpedition(get_gametime(), building.owner().player_number(), building));
 }
 
 void Game::send_player_enhance_building(Building& building, DescriptionIndex const id) {
 	assert(building.owner().tribe().has_building(id));
 
 	send_player_command(
-	   *new CmdEnhanceBuilding(get_gametime(), building.owner().player_number(), building, id));
+	   new CmdEnhanceBuilding(get_gametime(), building.owner().player_number(), building, id));
 }
 
 void Game::send_player_evict_worker(Worker& worker) {
-	send_player_command(*new CmdEvictWorker(get_gametime(), worker.owner().player_number(), worker));
+	send_player_command(new CmdEvictWorker(get_gametime(), worker.owner().player_number(), worker));
 }
 
 void Game::send_player_set_ware_priority(PlayerImmovable& imm,
@@ -791,14 +791,14 @@
                                          DescriptionIndex const index,
                                          int32_t const prio) {
 	send_player_command(
-	   *new CmdSetWarePriority(get_gametime(), imm.owner().player_number(), imm, type, index, prio));
+	   new CmdSetWarePriority(get_gametime(), imm.owner().player_number(), imm, type, index, prio));
 }
 
 void Game::send_player_set_input_max_fill(PlayerImmovable& imm,
                                           DescriptionIndex const index,
                                           WareWorker type,
                                           uint32_t const max_fill) {
-	send_player_command(*new CmdSetInputMaxFill(
+	send_player_command(new CmdSetInputMaxFill(
 	   get_gametime(), imm.owner().player_number(), imm, index, type, max_fill));
 }
 
@@ -806,17 +806,17 @@
                                                TrainingAttribute attr,
                                                int32_t const val) {
 	send_player_command(
-	   *new CmdChangeTrainingOptions(get_gametime(), ts.owner().player_number(), ts, attr, val));
+	   new CmdChangeTrainingOptions(get_gametime(), ts.owner().player_number(), ts, attr, val));
 }
 
 void Game::send_player_drop_soldier(Building& b, int32_t const ser) {
 	assert(ser != -1);
-	send_player_command(*new CmdDropSoldier(get_gametime(), b.owner().player_number(), b, ser));
+	send_player_command(new CmdDropSoldier(get_gametime(), b.owner().player_number(), b, ser));
 }
 
 void Game::send_player_change_soldier_capacity(Building& b, int32_t const val) {
 	send_player_command(
-	   *new CmdChangeSoldierCapacity(get_gametime(), b.owner().player_number(), b, val));
+	   new CmdChangeSoldierCapacity(get_gametime(), b.owner().player_number(), b, val));
 }
 
 void Game::send_player_enemyflagaction(const Flag& flag,
@@ -824,31 +824,31 @@
                                        uint32_t const num_soldiers) {
 	if (1 < player(who_attacks)
 	           .vision(Map::get_index(flag.get_building()->get_position(), map().get_width())))
-		send_player_command(*new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, num_soldiers));
+		send_player_command(new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, num_soldiers));
 }
 
 void Game::send_player_ship_scouting_direction(Ship& ship, WalkingDir direction) {
-	send_player_command(*new CmdShipScoutDirection(
+	send_player_command(new CmdShipScoutDirection(
 	   get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction));
 }
 
 void Game::send_player_ship_construct_port(Ship& ship, Coords coords) {
-	send_player_command(*new CmdShipConstructPort(
+	send_player_command(new CmdShipConstructPort(
 	   get_gametime(), ship.get_owner()->player_number(), ship.serial(), coords));
 }
 
 void Game::send_player_ship_explore_island(Ship& ship, IslandExploreDirection direction) {
-	send_player_command(*new CmdShipExploreIsland(
+	send_player_command(new CmdShipExploreIsland(
 	   get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction));
 }
 
 void Game::send_player_sink_ship(Ship& ship) {
 	send_player_command(
-	   *new CmdShipSink(get_gametime(), ship.get_owner()->player_number(), ship.serial()));
+	   new CmdShipSink(get_gametime(), ship.get_owner()->player_number(), ship.serial()));
 }
 
 void Game::send_player_cancel_expedition_ship(Ship& ship) {
-	send_player_command(*new CmdShipCancelExpedition(
+	send_player_command(new CmdShipCancelExpedition(
 	   get_gametime(), ship.get_owner()->player_number(), ship.serial()));
 }
 
@@ -856,7 +856,7 @@
 	auto* object = objects().get_object(trade.initiator);
 	assert(object != nullptr);
 	send_player_command(
-	   *new CmdProposeTrade(get_gametime(), object->get_owner()->player_number(), trade));
+	   new CmdProposeTrade(get_gametime(), object->get_owner()->player_number(), trade));
 }
 
 int Game::propose_trade(const Trade& trade) {

=== modified file 'src/logic/game.h'
--- src/logic/game.h	2019-03-01 16:24:48 +0000
+++ src/logic/game.h	2019-05-01 07:38:30 +0000
@@ -248,7 +248,7 @@
 
 	void enqueue_command(Command* const);
 
-	void send_player_command(Widelands::PlayerCommand&);
+	void send_player_command(Widelands::PlayerCommand*);
 
 	void send_player_bulldoze(PlayerImmovable&, bool recurse = false);
 	void send_player_dismantle(PlayerImmovable&);

=== modified file 'src/logic/game_controller.h'
--- src/logic/game_controller.h	2019-02-23 11:00:49 +0000
+++ src/logic/game_controller.h	2019-05-01 07:38:30 +0000
@@ -47,7 +47,9 @@
 	}
 
 	virtual void think() = 0;
-	virtual void send_player_command(Widelands::PlayerCommand&) = 0;
+
+	// TODO(Klaus Halfmann): Command must be deleted once it was handled.
+	virtual void send_player_command(Widelands::PlayerCommand*) = 0;
 	virtual int32_t get_frametime() = 0;
 	virtual GameType get_game_type() = 0;
 

=== modified file 'src/logic/replay_game_controller.cc'
--- src/logic/replay_game_controller.cc	2019-02-23 11:00:49 +0000
+++ src/logic/replay_game_controller.cc	2019-05-01 07:38:30 +0000
@@ -60,7 +60,7 @@
 	}
 }
 
-void ReplayGameController::send_player_command(Widelands::PlayerCommand&) {
+void ReplayGameController::send_player_command(Widelands::PlayerCommand*) {
 	throw wexception("Trying to send a player command during replay");
 }
 

=== modified file 'src/logic/replay_game_controller.h'
--- src/logic/replay_game_controller.h	2019-02-23 11:00:49 +0000
+++ src/logic/replay_game_controller.h	2019-05-01 07:38:30 +0000
@@ -35,7 +35,7 @@
 
 	void think() override;
 
-	void send_player_command(Widelands::PlayerCommand&) override;
+	void send_player_command(Widelands::PlayerCommand*) override;
 	int32_t get_frametime() override;
 	GameController::GameType get_game_type() override;
 	uint32_t real_speed() override;

=== modified file 'src/logic/single_player_game_controller.cc'
--- src/logic/single_player_game_controller.cc	2019-02-23 11:00:49 +0000
+++ src/logic/single_player_game_controller.cc	2019-05-01 07:38:30 +0000
@@ -74,9 +74,9 @@
 	}
 }
 
-void SinglePlayerGameController::send_player_command(Widelands::PlayerCommand& pc) {
-	pc.set_cmdserial(++player_cmdserial_);
-	game_.enqueue_command(&pc);
+void SinglePlayerGameController::send_player_command(Widelands::PlayerCommand* pc) {
+	pc->set_cmdserial(++player_cmdserial_);
+	game_.enqueue_command(pc);
 }
 
 int32_t SinglePlayerGameController::get_frametime() {

=== modified file 'src/logic/single_player_game_controller.h'
--- src/logic/single_player_game_controller.h	2019-02-23 11:00:49 +0000
+++ src/logic/single_player_game_controller.h	2019-05-01 07:38:30 +0000
@@ -30,7 +30,7 @@
 	~SinglePlayerGameController() override;
 
 	void think() override;
-	void send_player_command(Widelands::PlayerCommand&) override;
+	void send_player_command(Widelands::PlayerCommand*) override;
 	int32_t get_frametime() override;
 	GameController::GameType get_game_type() override;
 	uint32_t real_speed() override;

=== modified file 'src/network/gameclient.cc'
--- src/network/gameclient.cc	2019-04-29 16:22:08 +0000
+++ src/network/gameclient.cc	2019-05-01 07:38:30 +0000
@@ -91,8 +91,115 @@
 
 	/// Backlog of chat messages
 	std::vector<ChatMessage> chatmessages;
+
+
+	void send_hello();
+	void send_player_command(Widelands::PlayerCommand*);
+
+	bool run_map_menu(GameClient* This, bool internet);
+	void run_game(InteractiveGameBase* igb, UI::ProgressWindow*, bool internet);
+
+	InteractiveGameBase* init_game(GameClient* This, UI::ProgressWindow*);
+
 };
 
+void GameClientImpl::send_hello() {
+	SendPacket s;
+	s.unsigned_8(NETCMD_HELLO);
+	s.unsigned_8(NETWORK_PROTOCOL_VERSION);
+	s.string(localplayername);
+	s.string(build_id());
+	net->send(s);
+}
+
+void GameClientImpl::send_player_command(Widelands::PlayerCommand* pc) {
+	SendPacket s;
+	s.unsigned_8(NETCMD_PLAYERCOMMAND);
+	s.signed_32(game->get_gametime());
+	pc->serialize(s);
+	net->send(s);
+}
+
+/**
+ * Show and run() the fullscreen menu for setting map and mapsettings.
+ *
+ *  @return true to indicate that run is done.
+ */
+bool GameClientImpl::run_map_menu(GameClient* This, bool internet) {
+	FullscreenMenuLaunchMPG lgm(This, This);
+	lgm.set_chat_provider(*This);
+	modal = &lgm;
+	FullscreenMenuBase::MenuTarget code = lgm.run<FullscreenMenuBase::MenuTarget>();
+	modal = nullptr;
+	if (code == FullscreenMenuBase::MenuTarget::kBack) {
+		// if this is an internet game, tell the metaserver that client is back in the lobby.
+		if (internet) {
+			InternetGaming::ref().set_game_done();
+		}
+		return true;
+	}
+	return false;
+}
+
+/**
+ * Show Pogressdialog and load map or saved game.
+ */
+InteractiveGameBase* GameClientImpl::init_game(GameClient* This, UI::ProgressWindow* loader) {
+	modal = loader;
+	std::vector<std::string> tipstext;
+	tipstext.push_back("general_game");
+	tipstext.push_back("multiplayer");
+	try {
+		tipstext.push_back(This->get_players_tribe());
+	} catch (GameClient::NoTribe) {
+	}
+	GameTips tips(*loader, tipstext);
+
+	loader->step(_("Preparing game"));
+
+	game->set_game_controller(This);
+	uint8_t const pn = settings.playernum + 1;
+	game->save_handler().set_autosave_filename(
+					(boost::format("%s_netclient%u") % kAutosavePrefix % static_cast<unsigned int>(pn)).str());
+	InteractiveGameBase* igb;
+	if (pn > 0)
+		igb = new InteractivePlayer(*game, g_options.pull_section("global"), pn, true);
+	else
+		igb = new InteractiveSpectator(*game, g_options.pull_section("global"), true);
+	game -> set_ibase(igb);
+	igb->set_chat_provider(*This);
+	if (settings.savegame) {  //  new map
+		game->init_newgame(loader, settings);
+	} else {  // savegame
+		game->init_savegame(loader, settings);
+	}
+	return igb;
+}
+
+
+/**
+ * Run the actual game and cleanup when done.
+ */
+void GameClientImpl::run_game(InteractiveGameBase* igb, UI::ProgressWindow* loader, bool internet) {
+	time.reset(game->get_gametime());
+	lasttimestamp = game->get_gametime();
+	lasttimestamp_realtime = SDL_GetTicks();
+
+	modal = igb;
+	game->run(
+			 loader,
+			 settings.savegame ?
+			 Widelands::Game::Loaded :
+			 settings.scenario ? Widelands::Game::NewMPScenario : Widelands::Game::NewNonScenario,
+			 "", false, (boost::format("netclient_%d") % static_cast<int>(settings.usernum)).str());
+
+	// if this is an internet game, tell the metaserver that the game is done.
+	if (internet)
+		InternetGaming::ref().set_game_done();
+	modal = nullptr;
+	game = nullptr;
+}
+
 GameClient::GameClient(const std::pair<NetAddress, NetAddress>& host,
                        const std::string& playername,
                        bool internet,
@@ -141,85 +248,32 @@
 	delete d;
 }
 
+
+
 void GameClient::run() {
-	SendPacket s;
-	s.unsigned_8(NETCMD_HELLO);
-	s.unsigned_8(NETWORK_PROTOCOL_VERSION);
-	s.string(d->localplayername);
-	s.string(build_id());
-	d->net->send(s);
 
+	d->send_hello();
 	d->settings.multiplayer = true;
 
 	// Fill the list of possible system messages
 	NetworkGamingMessages::fill_map();
-	{
-		FullscreenMenuLaunchMPG lgm(this, this);
-		lgm.set_chat_provider(*this);
-		d->modal = &lgm;
-		FullscreenMenuBase::MenuTarget code = lgm.run<FullscreenMenuBase::MenuTarget>();
-		d->modal = nullptr;
-		if (code == FullscreenMenuBase::MenuTarget::kBack) {
-			// if this is an internet game, tell the metaserver that client is back in the lobby.
-			if (internet_)
-				InternetGaming::ref().set_game_done();
-			return;
-		}
-	}
+
+	if (d->run_map_menu(this, internet_))
+	    return; // did not select a Map ...
 
 	d->server_is_waiting = true;
 
+	bool writeSyncStreams = g_options.pull_section("global").get_bool("write_syncstreams", true);
 	Widelands::Game game;
-	game.set_write_syncstream(g_options.pull_section("global").get_bool("write_syncstreams", true));
+	game.set_write_syncstream(writeSyncStreams);
 
 	try {
 		std::unique_ptr<UI::ProgressWindow> loader_ui(new UI::ProgressWindow());
-		d->modal = loader_ui.get();
-		std::vector<std::string> tipstext;
-		tipstext.push_back("general_game");
-		tipstext.push_back("multiplayer");
-		try {
-			tipstext.push_back(get_players_tribe());
-		} catch (NoTribe) {
-		}
-		GameTips tips(*loader_ui.get(), tipstext);
-
-		loader_ui->step(_("Preparing game"));
 
 		d->game = &game;
-		game.set_game_controller(this);
-		uint8_t const pn = d->settings.playernum + 1;
-		game.save_handler().set_autosave_filename(
-		   (boost::format("%s_netclient%u") % kAutosavePrefix % static_cast<unsigned int>(pn)).str());
-		InteractiveGameBase* igb;
-		if (pn > 0)
-			igb = new InteractivePlayer(game, g_options.pull_section("global"), pn, true);
-		else
-			igb = new InteractiveSpectator(game, g_options.pull_section("global"), true);
-		game.set_ibase(igb);
-		igb->set_chat_provider(*this);
-		if (!d->settings.savegame) {  //  new map
-			game.init_newgame(loader_ui.get(), d->settings);
-		} else {  // savegame
-			game.init_savegame(loader_ui.get(), d->settings);
-		}
-		d->time.reset(game.get_gametime());
-		d->lasttimestamp = game.get_gametime();
-		d->lasttimestamp_realtime = SDL_GetTicks();
-
-		d->modal = igb;
-		game.run(
-		   loader_ui.get(),
-		   d->settings.savegame ?
-		      Widelands::Game::Loaded :
-		      d->settings.scenario ? Widelands::Game::NewMPScenario : Widelands::Game::NewNonScenario,
-		   "", false, (boost::format("netclient_%d") % static_cast<int>(d->settings.usernum)).str());
-
-		// if this is an internet game, tell the metaserver that the game is done.
-		if (internet_)
-			InternetGaming::ref().set_game_done();
-		d->modal = nullptr;
-		d->game = nullptr;
+		InteractiveGameBase* igb = d->init_game(this, loader_ui.get());
+		d->run_game(igb, loader_ui.get(), internet_);
+
 	} catch (...) {
 		WLApplication::emergency_save(game);
 		d->game = nullptr;
@@ -237,6 +291,7 @@
 	handle_network();
 
 	if (d->game) {
+		// TODO(Klaus Halfmann): what kind of time tricks are done here?
 		if (d->realspeed == 0 || d->server_is_waiting)
 			d->time.fastforward();
 		else
@@ -254,25 +309,28 @@
 	}
 }
 
-void GameClient::send_player_command(Widelands::PlayerCommand& pc) {
+/**
+ * Send PlayerCommand to server.
+ *
+ * @param pc will always be deleted in the end.
+ */
+void GameClient::send_player_command(Widelands::PlayerCommand* pc) {
 	assert(d->game);
-	if (pc.sender() != d->settings.playernum + 1) {
-		delete &pc;
-		return;
+
+	// TODDO(Klaus Halfmann)should this be an assert?
+	if (pc->sender() == d->settings.playernum + 1) //  allow command for current player only
+	{
+		log("[Client]: send playercommand at time %i\n", d->game->get_gametime());
+
+		d->send_player_command(pc);
+
+		d->lasttimestamp = d->game->get_gametime();
+		d->lasttimestamp_realtime = SDL_GetTicks();
+	} else {
+		log("[Client]: Playercommand is not for curret player? %i\n", pc -> sender());
 	}
 
-	log("[Client]: send playercommand at time %i\n", d->game->get_gametime());
-
-	SendPacket s;
-	s.unsigned_8(NETCMD_PLAYERCOMMAND);
-	s.signed_32(d->game->get_gametime());
-	pc.serialize(s);
-	d->net->send(s);
-
-	d->lasttimestamp = d->game->get_gametime();
-	d->lasttimestamp_realtime = SDL_GetTicks();
-
-	delete &pc;
+	delete pc;
 }
 
 int32_t GameClient::get_frametime() {
@@ -544,6 +602,331 @@
 	}
 }
 
+void GameClient::handle_disconnect(RecvPacket& packet) {
+	uint8_t number = packet.unsigned_8();
+	std::string reason = packet.string();
+	if (number == 1)
+		disconnect(reason, "", false);
+	else {
+		std::string arg = packet.string();
+		disconnect(reason, arg, false);
+	}
+}
+
+/**
+ * Hello from the other side
+ */
+void GameClient::handle_hello(RecvPacket& packet) {
+	if (d->settings.usernum == -2) // TODDO(Klaus Halfmann): what magic number is this?
+		throw ProtocolException(NETCMD_HELLO);
+	uint8_t const version = packet.unsigned_8();
+	if (version != NETWORK_PROTOCOL_VERSION)
+		throw DisconnectException("DIFFERENT_PROTOCOL_VERS");
+	d->settings.usernum = packet.unsigned_32();
+	d->settings.playernum = -1;
+}
+
+/**
+ * Give a pong for a ping
+ */
+void GameClient::handle_ping(RecvPacket&) {
+	SendPacket s;
+	s.unsigned_8(NETCMD_PONG);
+	d->net->send(s);
+
+	log("[Client] Pong!\n");
+}
+
+/**
+ * New Map name was send.
+ */
+void GameClient::handle_setting_map(RecvPacket& packet) {
+	d->settings.mapname = packet.string();
+	d->settings.mapfilename = g_fs->FileSystem::fix_cross_file(packet.string());
+	d->settings.savegame = packet.unsigned_8() == 1;
+	d->settings.scenario = packet.unsigned_8() == 1;
+	log("[Client] SETTING_MAP '%s' '%s'\n", d->settings.mapname.c_str(),
+		d->settings.mapfilename.c_str());
+
+	// New map was set, so we clean up the buffer of a previously requested file
+	file_.reset(nullptr);
+}
+
+/**
+ *
+ */
+void GameClient::handle_new_file(RecvPacket& packet) {
+	std::string path = g_fs->FileSystem::fix_cross_file(packet.string());
+	uint32_t bytes = packet.unsigned_32();
+	std::string md5 = packet.string();
+
+	// Check whether the file or a file with that name already exists
+	if (g_fs->file_exists(path)) {
+		// If the file is a directory, we have to rename the file and replace it with the version
+		// of thehost. If it is a ziped file, we can check, whether the host and the client have
+		// got the same file.
+		if (!g_fs->is_directory(path)) {
+			FileRead fr;
+			fr.open(*g_fs, path);
+			if (bytes == fr.get_size()) {
+				std::unique_ptr<char[]> complete(new char[bytes]);
+				if (!complete)
+					throw wexception("Out of memory");
+
+				fr.data_complete(complete.get(), bytes);
+				// TODO(Klaus Halfmann): compute MD5 on the fly in FileRead...
+				SimpleMD5Checksum md5sum;
+				md5sum.data(complete.get(), bytes);
+				md5sum.finish_checksum();
+				std::string localmd5 = md5sum.get_checksum().str();
+				if (localmd5 == md5)
+					// everything is alright we now have the file.
+					return;
+			}
+		}
+		// Don't overwrite the file, better rename the original one
+		try {
+			g_fs->fs_rename(path, backup_file_name(path));
+		} catch (const FileError& e) {
+			log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
+				"%s\n",
+				e.what());
+			// TODO(Arty): What now? It just means the next step will fail
+			// or possibly result in some corrupt file
+		}
+	}
+
+	// Yes we need the file!
+	SendPacket s;
+	s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
+	d->net->send(s);
+
+	file_.reset(new NetTransferFile());
+	file_->bytes = bytes;
+	file_->filename = path;
+	file_->md5sum = md5;
+	size_t position = path.rfind(g_fs->file_separator(), path.size() - 2);
+	if (position != std::string::npos) {
+		path.resize(position);
+		g_fs->ensure_directory_exists(path);
+	}
+}
+
+/**
+ *
+ */
+void GameClient::handle_file_part(RecvPacket& packet) {
+	// Only go on, if we are waiting for a file part at the moment. It can happen, that an
+	// "unrequested" part is send by the server if the map was changed just a moment ago
+	// and there was an outstanding request from the client.
+	if (!file_)
+		return;  // silently ignore
+
+	uint32_t part = packet.unsigned_32();
+	uint32_t size = packet.unsigned_32();
+
+	// Send an answer
+	SendPacket s;
+	s.unsigned_8(NETCMD_FILE_PART);
+	s.unsigned_32(part);
+	s.string(file_->md5sum);
+	d->net->send(s);
+
+	FilePart fp;
+
+	char buf[NETFILEPARTSIZE];
+	assert(size <= NETFILEPARTSIZE);
+
+	// TODO(Klaus Halfmann): read directcly into FilePart?
+	if (packet.data(buf, size) != size)
+		log("Readproblem. Will try to go on anyways\n");
+	memcpy(fp.part, &buf[0], size);
+	file_->parts.push_back(fp);
+
+	// Write file to disk as soon as all parts arrived
+	uint32_t left = (file_->bytes - NETFILEPARTSIZE * part);
+	if (left <= NETFILEPARTSIZE) {
+		FileWrite fw;
+		left = file_->bytes;
+		uint32_t i = 0;
+		// Put all data together
+		while (left > 0) {
+			uint32_t writeout = (left > NETFILEPARTSIZE) ? NETFILEPARTSIZE : left;
+			fw.data(file_->parts[i].part, writeout, FileWrite::Pos::null());
+			left -= writeout;
+			++i;
+		}
+		// Now really write the file
+		fw.write(*g_fs, file_->filename.c_str());
+
+		// Check for consistence
+		FileRead fr;
+		fr.open(*g_fs, file_->filename);
+
+		std::unique_ptr<char[]> complete(new char[file_->bytes]);
+
+		fr.data_complete(complete.get(), file_->bytes);
+		SimpleMD5Checksum md5sum;
+		md5sum.data(complete.get(), file_->bytes);
+		md5sum.finish_checksum();
+		std::string localmd5 = md5sum.get_checksum().str();
+		if (localmd5 != file_->md5sum) {
+			// Something went wrong! We have to rerequest the file.
+			s.reset();
+			s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
+			d->net->send(s);
+			// Notify the players
+			s.reset();
+			s.unsigned_8(NETCMD_CHAT);
+			s.string(_("/me 's file failed md5 checksumming."));
+			d->net->send(s);
+			try {
+				g_fs->fs_unlink(file_->filename);
+			} catch (const FileError& e) {
+				log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
+					"%s\n",
+					e.what());
+			}
+		}
+		// Check file for validity
+		bool invalid = false;
+		if (d->settings.savegame) {
+			// Saved game check - does Widelands recognize the file as saved game?
+			Widelands::Game game;
+			try {
+				Widelands::GameLoader gl(file_->filename, game);
+			} catch (...) {
+				invalid = true;
+			}
+		} else {
+			// Map check - does Widelands recognize the file as map?
+			Widelands::Map map;
+			std::unique_ptr<Widelands::MapLoader> ml = map.get_correct_loader(file_->filename);
+			if (!ml)
+				invalid = true;
+		}
+		if (invalid) {
+			try {
+				g_fs->fs_unlink(file_->filename);
+				// Restore original file, if there was one before
+				if (g_fs->file_exists(backup_file_name(file_->filename)))
+					g_fs->fs_rename(backup_file_name(file_->filename), file_->filename);
+			} catch (const FileError& e) {
+				log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
+					"%s\n",
+					e.what());
+			}
+			s.reset();
+			s.unsigned_8(NETCMD_CHAT);
+			s.string(_("/me checked the received file. Although md5 check summing succeeded, "
+					   "I can not handle the file."));
+			d->net->send(s);
+		}
+	}
+}
+
+/**
+ *
+ */
+void GameClient::handle_setting_tribes(RecvPacket& packet) {
+	d->settings.tribes.clear();
+	for (uint8_t i = packet.unsigned_8(); i; --i) {
+		Widelands::TribeBasicInfo info = Widelands::get_tribeinfo(packet.string());
+
+		// Get initializations (we have to do this locally, for translations)
+		LuaInterface lua;
+		info.initializations.clear();
+		for (uint8_t j = packet.unsigned_8(); j > 0; --j) {
+			std::string const initialization_script = packet.string();
+			std::unique_ptr<LuaTable> t = lua.run_script(initialization_script);
+			t->do_not_warn_about_unaccessed_keys();
+			info.initializations.push_back(Widelands::TribeBasicInfo::Initialization(
+				initialization_script, t->get_string("descname"), t->get_string("tooltip")));
+		}
+		d->settings.tribes.push_back(info);
+	}
+}
+
+/**
+ *
+ */
+void GameClient::handle_setting_allplayers(RecvPacket& packet) {
+	d->settings.players.resize(packet.unsigned_8());
+	for (uint8_t i = 0; i < d->settings.players.size(); ++i) {
+		receive_one_player(i, packet);
+	}
+	// Map changes are finished here
+	Notifications::publish(NoteGameSettings(NoteGameSettings::Action::kMap));
+}
+
+/**
+ *
+ */
+void GameClient::handle_playercommand(RecvPacket& packet) {
+	if (!d->game)
+		throw DisconnectException("PLAYERCMD_WO_GAME");
+
+	int32_t const time = packet.signed_32();
+	Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(packet);
+	plcmd.set_duetime(time);
+	d->game->enqueue_command(&plcmd);
+	d->time.receive(time);
+}
+
+/**
+ *
+ */
+void GameClient::handle_syncrequest(RecvPacket& packet) {
+	if (!d->game)
+		throw DisconnectException("SYNCREQUEST_WO_GAME");
+	int32_t const time = packet.signed_32();
+	d->time.receive(time);
+	d->game->enqueue_command(new CmdNetCheckSync(time, [this] { sync_report_callback(); }));
+	d->game->report_sync_request();
+}
+
+/**
+ *
+ */
+void GameClient::handle_chat(RecvPacket& packet) {
+	ChatMessage c("");
+	c.playern = packet.signed_16();
+	c.sender  = packet.string();
+	c.msg     = packet.string();
+	if (packet.unsigned_8())
+		c.recipient = packet.string();
+	d->chatmessages.push_back(c);
+	Notifications::publish(c);
+}
+
+/**
+ *
+ */
+void GameClient::handle_system_message(RecvPacket& packet) {
+	const std::string code = packet.string();
+	const std::string arg1 = packet.string();
+	const std::string arg2 = packet.string();
+	const std::string arg3 = packet.string();
+	ChatMessage c(NetworkGamingMessages::get_message(code, arg1, arg2, arg3));
+	c.playern = UserSettings::none();  //  == System message
+									   // c.sender remains empty to indicate a system message
+	d->chatmessages.push_back(c);
+	Notifications::publish(c);
+}
+
+/**
+ *
+ */
+void GameClient::handle_desync(RecvPacket&) {
+	log("[Client] received NETCMD_INFO_DESYNC. Trying to salvage some "
+		"information for debugging.\n");
+	if (d->game) {
+		d->game->save_syncstream(true);
+		// We don't know our playernumber, so report as -1
+		d->game->report_desync(-1);
+	}
+}
+
 /**
  * Handle one packet received from the host.
  *
@@ -552,363 +935,91 @@
 void GameClient::handle_packet(RecvPacket& packet) {
 	uint8_t cmd = packet.unsigned_8();
 
-	if (cmd == NETCMD_DISCONNECT) {
-		uint8_t number = packet.unsigned_8();
-		std::string reason = packet.string();
-		if (number == 1)
-			disconnect(reason, "", false);
-		else {
-			std::string arg = packet.string();
-			disconnect(reason, arg, false);
-		}
-		return;
-	}
-
-	if (d->settings.usernum == -2) {
-		if (cmd != NETCMD_HELLO)
+	switch (cmd) {
+		case NETCMD_DISCONNECT:
+			return handle_disconnect(packet);
+		case NETCMD_HELLO:
+			return handle_hello(packet);
+		case NETCMD_PING:
+			handle_ping(packet);
+			break;
+		case NETCMD_SETTING_MAP:
+			handle_setting_map(packet);
+			break;
+		case NETCMD_NEW_FILE_AVAILABLE:
+			handle_new_file(packet);
+			break;
+		case NETCMD_FILE_PART:
+			handle_file_part(packet);
+			break;
+		case NETCMD_SETTING_TRIBES:
+			handle_setting_tribes(packet);
+			break;
+		case NETCMD_SETTING_ALLPLAYERS:
+			handle_setting_allplayers(packet);
+			break;
+		case NETCMD_SETTING_PLAYER: {
+			uint8_t player = packet.unsigned_8();
+			receive_one_player(player, packet);
+		}
+			break;
+		case NETCMD_SETTING_ALLUSERS: {
+			d->settings.users.resize(packet.unsigned_8());
+			for (uint32_t i = 0; i < d->settings.users.size(); ++i)
+				receive_one_user(i, packet);
+		}
+			break;
+		case NETCMD_SETTING_USER: {
+			uint32_t user = packet.unsigned_32();
+			receive_one_user(user, packet);
+		}
+			break;
+		case NETCMD_SET_PLAYERNUMBER: {
+			int32_t number = packet.signed_32();
+			d->settings.playernum = number;
+			d->settings.users.at(d->settings.usernum).position = number;
+		}
+			break;
+		case NETCMD_WIN_CONDITION:
+			d->settings.win_condition_script = g_fs->FileSystem::fix_cross_file(packet.string());
+			break;
+		case NETCMD_PEACEFUL_MODE:
+			d->settings.peaceful = packet.unsigned_8();
+			break;
+		case NETCMD_LAUNCH:
+			if (!d->modal || d->game) {
+				throw DisconnectException("UNEXPECTED_LAUNCH");
+			}
+			d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kOk);
+			break;
+		case NETCMD_SETSPEED:
+			d->realspeed = packet.unsigned_16();
+			log("[Client] speed: %u.%03u\n", d->realspeed / 1000, d->realspeed % 1000);
+			break;
+		case NETCMD_TIME:
+			d->time.receive(packet.signed_32());
+			break;
+		case NETCMD_WAIT:
+			log("[Client]: server is waiting.\n");
+			d->server_is_waiting = true;
+			break;
+		case NETCMD_PLAYERCOMMAND:
+			handle_playercommand(packet);
+			break;
+		case NETCMD_SYNCREQUEST:
+			handle_syncrequest(packet);
+			break;
+		case NETCMD_CHAT:
+			handle_chat(packet);
+			break;
+		case NETCMD_SYSTEM_MESSAGE_CODE:
+			handle_system_message(packet);
+			break;
+		case NETCMD_INFO_DESYNC:
+			handle_desync(packet);
+			break;
+		default:
 			throw ProtocolException(cmd);
-		uint8_t const version = packet.unsigned_8();
-		if (version != NETWORK_PROTOCOL_VERSION)
-			throw DisconnectException("DIFFERENT_PROTOCOL_VERS");
-		d->settings.usernum = packet.unsigned_32();
-		d->settings.playernum = -1;
-		return;
-	}
-
-	switch (cmd) {
-	case NETCMD_PING: {
-		SendPacket s;
-		s.unsigned_8(NETCMD_PONG);
-		d->net->send(s);
-
-		log("[Client] Pong!\n");
-		break;
-	}
-
-	case NETCMD_SETTING_MAP: {
-		d->settings.mapname = packet.string();
-		d->settings.mapfilename = g_fs->FileSystem::fix_cross_file(packet.string());
-		d->settings.savegame = packet.unsigned_8() == 1;
-		d->settings.scenario = packet.unsigned_8() == 1;
-		log("[Client] SETTING_MAP '%s' '%s'\n", d->settings.mapname.c_str(),
-		    d->settings.mapfilename.c_str());
-
-		// New map was set, so we clean up the buffer of a previously requested file
-		file_.reset(nullptr);
-		break;
-	}
-
-	case NETCMD_NEW_FILE_AVAILABLE: {
-		std::string path = g_fs->FileSystem::fix_cross_file(packet.string());
-		uint32_t bytes = packet.unsigned_32();
-		std::string md5 = packet.string();
-
-		// Check whether the file or a file with that name already exists
-		if (g_fs->file_exists(path)) {
-			// If the file is a directory, we have to rename the file and replace it with the version
-			// of the
-			// host. If it is a ziped file, we can check, whether the host and the client have got the
-			// same file.
-			if (!g_fs->is_directory(path)) {
-				FileRead fr;
-				fr.open(*g_fs, path);
-				if (bytes == fr.get_size()) {
-					std::unique_ptr<char[]> complete(new char[bytes]);
-					if (!complete)
-						throw wexception("Out of memory");
-
-					fr.data_complete(complete.get(), bytes);
-					SimpleMD5Checksum md5sum;
-					md5sum.data(complete.get(), bytes);
-					md5sum.finish_checksum();
-					std::string localmd5 = md5sum.get_checksum().str();
-					if (localmd5 == md5)
-						// everything is alright we already have the file.
-						return;
-				}
-			}
-			// Don't overwrite the file, better rename the original one
-			try {
-				g_fs->fs_rename(path, backup_file_name(path));
-			} catch (const FileError& e) {
-				log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
-				    "%s\n",
-				    e.what());
-				// TODO(Arty): What now? It just means the next step will fail
-				// or possibly result in some corrupt file
-			}
-		}
-
-		// Yes we need the file!
-		SendPacket s;
-		s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
-		d->net->send(s);
-
-		file_.reset(new NetTransferFile());
-		file_->bytes = bytes;
-		file_->filename = path;
-		file_->md5sum = md5;
-		size_t position = path.rfind(g_fs->file_separator(), path.size() - 2);
-		if (position != std::string::npos) {
-			path.resize(position);
-			g_fs->ensure_directory_exists(path);
-		}
-		break;
-	}
-
-	case NETCMD_FILE_PART: {
-		// Only go on, if we are waiting for a file part at the moment. It can happen, that an
-		// "unrequested"
-		// part is send by the server if the map was changed just a moment ago and there was an
-		// outstanding
-		// request from the client.
-		if (!file_)
-			return;  // silently ignore
-
-		uint32_t part = packet.unsigned_32();
-		uint32_t size = packet.unsigned_32();
-
-		// Send an answer
-		SendPacket s;
-		s.unsigned_8(NETCMD_FILE_PART);
-		s.unsigned_32(part);
-		s.string(file_->md5sum);
-		d->net->send(s);
-
-		FilePart fp;
-
-		char buf[NETFILEPARTSIZE];
-		assert(size <= NETFILEPARTSIZE);
-
-		if (packet.data(buf, size) != size)
-			log("Readproblem. Will try to go on anyways\n");
-		memcpy(fp.part, &buf[0], size);
-		file_->parts.push_back(fp);
-
-		// Write file to disk as soon as all parts arrived
-		uint32_t left = (file_->bytes - NETFILEPARTSIZE * part);
-		if (left <= NETFILEPARTSIZE) {
-			FileWrite fw;
-			left = file_->bytes;
-			uint32_t i = 0;
-			// Put all data together
-			while (left > 0) {
-				uint32_t writeout = (left > NETFILEPARTSIZE) ? NETFILEPARTSIZE : left;
-				fw.data(file_->parts[i].part, writeout, FileWrite::Pos::null());
-				left -= writeout;
-				++i;
-			}
-			// Now really write the file
-			fw.write(*g_fs, file_->filename.c_str());
-
-			// Check for consistence
-			FileRead fr;
-			fr.open(*g_fs, file_->filename);
-
-			std::unique_ptr<char[]> complete(new char[file_->bytes]);
-
-			fr.data_complete(complete.get(), file_->bytes);
-			SimpleMD5Checksum md5sum;
-			md5sum.data(complete.get(), file_->bytes);
-			md5sum.finish_checksum();
-			std::string localmd5 = md5sum.get_checksum().str();
-			if (localmd5 != file_->md5sum) {
-				// Something went wrong! We have to rerequest the file.
-				s.reset();
-				s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
-				d->net->send(s);
-				// Notify the players
-				s.reset();
-				s.unsigned_8(NETCMD_CHAT);
-				s.string(_("/me 's file failed md5 checksumming."));
-				d->net->send(s);
-				try {
-					g_fs->fs_unlink(file_->filename);
-				} catch (const FileError& e) {
-					log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
-					    "%s\n",
-					    e.what());
-				}
-			}
-			// Check file for validity
-			bool invalid = false;
-			if (d->settings.savegame) {
-				// Saved game check - does Widelands recognize the file as saved game?
-				Widelands::Game game;
-				try {
-					Widelands::GameLoader gl(file_->filename, game);
-				} catch (...) {
-					invalid = true;
-				}
-			} else {
-				// Map check - does Widelands recognize the file as map?
-				Widelands::Map map;
-				std::unique_ptr<Widelands::MapLoader> ml = map.get_correct_loader(file_->filename);
-				if (!ml)
-					invalid = true;
-			}
-			if (invalid) {
-				try {
-					g_fs->fs_unlink(file_->filename);
-					// Restore original file, if there was one before
-					if (g_fs->file_exists(backup_file_name(file_->filename)))
-						g_fs->fs_rename(backup_file_name(file_->filename), file_->filename);
-				} catch (const FileError& e) {
-					log("file error in GameClient::handle_packet: case NETCMD_FILE_PART: "
-					    "%s\n",
-					    e.what());
-				}
-				s.reset();
-				s.unsigned_8(NETCMD_CHAT);
-				s.string(_("/me checked the received file. Although md5 check summing succeeded, "
-				           "I can not handle the file."));
-				d->net->send(s);
-			}
-		}
-		break;
-	}
-
-	case NETCMD_SETTING_TRIBES: {
-		d->settings.tribes.clear();
-		for (uint8_t i = packet.unsigned_8(); i; --i) {
-			Widelands::TribeBasicInfo info = Widelands::get_tribeinfo(packet.string());
-
-			// Get initializations (we have to do this locally, for translations)
-			LuaInterface lua;
-			info.initializations.clear();
-			for (uint8_t j = packet.unsigned_8(); j; --j) {
-				std::string const initialization_script = packet.string();
-				std::unique_ptr<LuaTable> t = lua.run_script(initialization_script);
-				t->do_not_warn_about_unaccessed_keys();
-				info.initializations.push_back(Widelands::TribeBasicInfo::Initialization(
-				   initialization_script, t->get_string("descname"), t->get_string("tooltip")));
-			}
-			d->settings.tribes.push_back(info);
-		}
-		break;
-	}
-
-	case NETCMD_SETTING_ALLPLAYERS: {
-		d->settings.players.resize(packet.unsigned_8());
-		for (uint8_t i = 0; i < d->settings.players.size(); ++i) {
-			receive_one_player(i, packet);
-		}
-		// Map changes are finished here
-		Notifications::publish(NoteGameSettings(NoteGameSettings::Action::kMap));
-		break;
-	}
-	case NETCMD_SETTING_PLAYER: {
-		uint8_t player = packet.unsigned_8();
-		receive_one_player(player, packet);
-		break;
-	}
-	case NETCMD_SETTING_ALLUSERS: {
-		d->settings.users.resize(packet.unsigned_8());
-		for (uint32_t i = 0; i < d->settings.users.size(); ++i)
-			receive_one_user(i, packet);
-		break;
-	}
-	case NETCMD_SETTING_USER: {
-		uint32_t user = packet.unsigned_32();
-		receive_one_user(user, packet);
-		break;
-	}
-	case NETCMD_SET_PLAYERNUMBER: {
-		int32_t number = packet.signed_32();
-		d->settings.playernum = number;
-		d->settings.users.at(d->settings.usernum).position = number;
-		break;
-	}
-	case NETCMD_WIN_CONDITION: {
-		d->settings.win_condition_script = g_fs->FileSystem::fix_cross_file(packet.string());
-		break;
-	}
-	case NETCMD_PEACEFUL_MODE: {
-		d->settings.peaceful = packet.unsigned_8();
-		break;
-	}
-
-	case NETCMD_LAUNCH: {
-		if (!d->modal || d->game) {
-			throw DisconnectException("UNEXPECTED_LAUNCH");
-		}
-		d->modal->end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kOk);
-		break;
-	}
-
-	case NETCMD_SETSPEED:
-		d->realspeed = packet.unsigned_16();
-		log("[Client] speed: %u.%03u\n", d->realspeed / 1000, d->realspeed % 1000);
-		break;
-
-	case NETCMD_TIME:
-		d->time.receive(packet.signed_32());
-		break;
-
-	case NETCMD_WAIT:
-		log("[Client]: server is waiting.\n");
-		d->server_is_waiting = true;
-		break;
-
-	case NETCMD_PLAYERCOMMAND: {
-		if (!d->game)
-			throw DisconnectException("PLAYERCMD_WO_GAME");
-
-		int32_t const time = packet.signed_32();
-		Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(packet);
-		plcmd.set_duetime(time);
-		d->game->enqueue_command(&plcmd);
-		d->time.receive(time);
-		break;
-	}
-
-	case NETCMD_SYNCREQUEST: {
-		if (!d->game)
-			throw DisconnectException("SYNCREQUEST_WO_GAME");
-		int32_t const time = packet.signed_32();
-		d->time.receive(time);
-		d->game->enqueue_command(new CmdNetCheckSync(time, [this] { sync_report_callback(); }));
-		d->game->report_sync_request();
-		break;
-	}
-
-	case NETCMD_CHAT: {
-		ChatMessage c("");
-		c.playern = packet.signed_16();
-		c.sender = packet.string();
-		c.msg = packet.string();
-		if (packet.unsigned_8())
-			c.recipient = packet.string();
-		d->chatmessages.push_back(c);
-		Notifications::publish(c);
-		break;
-	}
-
-	case NETCMD_SYSTEM_MESSAGE_CODE: {
-		const std::string code = packet.string();
-		const std::string arg1 = packet.string();
-		const std::string arg2 = packet.string();
-		const std::string arg3 = packet.string();
-		ChatMessage c(NetworkGamingMessages::get_message(code, arg1, arg2, arg3));
-		c.playern = UserSettings::none();  //  == System message
-		// c.sender remains empty to indicate a system message
-		d->chatmessages.push_back(c);
-		Notifications::publish(c);
-		break;
-	}
-
-	case NETCMD_INFO_DESYNC:
-		log("[Client] received NETCMD_INFO_DESYNC. Trying to salvage some "
-		    "information for debugging.\n");
-		if (d->game) {
-			d->game->save_syncstream(true);
-			// We don't know our playernumber, so report as -1
-			d->game->report_desync(-1);
-		}
-		break;
-
-	default:
-		throw ProtocolException(cmd);
 	}
 }
 

=== modified file 'src/network/gameclient.h'
--- src/network/gameclient.h	2019-04-29 16:22:08 +0000
+++ src/network/gameclient.h	2019-05-01 07:38:30 +0000
@@ -29,14 +29,16 @@
 #include "network/netclient_interface.h"
 
 struct GameClientImpl;
+class InteractiveGameBase;
 
-// TODO(unknown): Use composition instead of inheritance
 /**
  * GameClient manages the lifetime of a network game in which this computer
  * participates as a client.
  *
  * This includes running the game setup screen and the actual game after
  * launch, as well as dealing with the actual network protocol.
+ *
+ * @param internet TODO(Klaus Halfmann): true: coonnect into the open internet via proxy, false connect locally / via IP.
  */
 struct GameClient : public GameController, public GameSettingsProvider, public ChatProvider {
 	GameClient(const std::pair<NetAddress, NetAddress>& host,
@@ -50,7 +52,7 @@
 
 	// GameController interface
 	void think() override;
-	void send_player_command(Widelands::PlayerCommand&) override;
+	void send_player_command(Widelands::PlayerCommand*) override;
 	int32_t get_frametime() override;
 	GameController::GameType get_game_type() override;
 
@@ -115,7 +117,21 @@
 
 	void sync_report_callback();
 
-	void handle_packet(RecvPacket&);
+	void handle_hello(RecvPacket& packet);
+	void handle_disconnect(RecvPacket& packet);
+	void handle_ping(RecvPacket& packet);
+	void handle_new_file(RecvPacket& packet);
+	void handle_syncrequest(RecvPacket& packet);
+	void handle_setting_map(RecvPacket& packet);
+	void handle_file_part(RecvPacket& packet);
+	void handle_setting_tribes(RecvPacket& packet);
+	void handle_setting_allplayers(RecvPacket& packet);
+	void handle_playercommand(RecvPacket& packet);
+	void handle_chat(RecvPacket& packet);
+	void handle_system_message(RecvPacket& packet);
+	void handle_desync(RecvPacket& packet);
+	void handle_packet(RecvPacket& packet);
+
 	void handle_network();
 	void send_time();
 	void receive_one_player(uint8_t number, StreamRead&);
@@ -125,8 +141,11 @@
 	                bool sendreason = true,
 	                bool showmsg = true);
 
+
+	GameClientImpl* d;
+
+	/** File that is eventually transferred via the netowrk if not found at the other side */
 	std::unique_ptr<NetTransferFile> file_;
-	GameClientImpl* d;
 	bool internet_;
 };
 

=== modified file 'src/network/gamehost.cc'
--- src/network/gamehost.cc	2019-04-29 16:22:08 +0000
+++ src/network/gamehost.cc	2019-05-01 07:38:30 +0000
@@ -761,15 +761,15 @@
 	}
 }
 
-void GameHost::send_player_command(Widelands::PlayerCommand& pc) {
-	pc.set_duetime(d->committed_networktime + 1);
+void GameHost::send_player_command(Widelands::PlayerCommand* pc) {
+	pc->set_duetime(d->committed_networktime + 1);
 
 	SendPacket packet;
 	packet.unsigned_8(NETCMD_PLAYERCOMMAND);
-	packet.signed_32(pc.duetime());
-	pc.serialize(packet);
+	packet.signed_32(pc->duetime());
+	pc-> serialize(packet);
 	broadcast(packet);
-	d->game->enqueue_command(&pc);
+	d->game->enqueue_command(pc);
 
 	committed_network_time(d->committed_networktime + 1);
 }
@@ -2181,11 +2181,11 @@
 		if (!d->game)
 			throw DisconnectException("PLAYERCMD_WO_GAME");
 		int32_t time = r.signed_32();
-		Widelands::PlayerCommand& plcmd = *Widelands::PlayerCommand::deserialize(r);
+		Widelands::PlayerCommand* plcmd = Widelands::PlayerCommand::deserialize(r);
 		log("[Host]: Client %u (%u) sent player command %u for %u, time = %i\n", i, client.playernum,
-		    static_cast<unsigned int>(plcmd.id()), plcmd.sender(), time);
+		    static_cast<unsigned int>(plcmd->id()), plcmd->sender(), time);
 		receive_client_time(i, time);
-		if (plcmd.sender() != client.playernum + 1)
+		if (plcmd->sender() != client.playernum + 1)
 			throw DisconnectException("PLAYERCMD_FOR_OTHER");
 		send_player_command(plcmd);
 	} break;

=== modified file 'src/network/gamehost.h'
--- src/network/gamehost.h	2019-04-29 16:22:08 +0000
+++ src/network/gamehost.h	2019-05-01 07:38:30 +0000
@@ -51,7 +51,7 @@
 
 	// GameController interface
 	void think() override;
-	void send_player_command(Widelands::PlayerCommand&) override;
+	void send_player_command(Widelands::PlayerCommand*) override;
 	int32_t get_frametime() override;
 	GameController::GameType get_game_type() override;
 

=== modified file 'src/network/netclient_interface.h'
--- src/network/netclient_interface.h	2019-02-23 11:00:49 +0000
+++ src/network/netclient_interface.h	2019-05-01 07:38:30 +0000
@@ -27,6 +27,7 @@
 /**
  * NetClient manages the network connection for a network game in which this computer
  * participates as a client.
+ *
  * This class provides the interface all NetClient implementation have to follow.
  * Currently two implementations exists: A "real" NetClient for local games and a
  * NetClientProxy which relays commands over a relay server.
@@ -42,18 +43,21 @@
 
 	/**
 	 * Returns whether the client is connected.
+     +
 	 * \return \c true if the connection is open, \c false otherwise.
 	 */
 	virtual bool is_connected() const = 0;
 
 	/**
 	 * Closes the connection.
+     *
 	 * If you want to send a goodbye-message to the host, do so before calling this.
 	 */
 	virtual void close() = 0;
 
 	/**
 	 * Tries to receive a packet.
+     *
 	 * \return A pointer to a packet if one packet is available, an invalid pointer otherwise.
 	 *   Calling this on a closed connection will return an invalid pointer.
 	 */
@@ -61,6 +65,7 @@
 
 	/**
 	 * Sends a packet.
+     *
 	 * Calling this on a closed connection will silently fail.
 	 * \param packet The packet to send.
 	 */

=== modified file 'src/wui/economy_options_window.cc'
--- src/wui/economy_options_window.cc	2019-02-23 11:00:49 +0000
+++ src/wui/economy_options_window.cc	2019-05-01 07:38:30 +0000
@@ -189,11 +189,11 @@
 			// Don't allow negative new amount.
 			if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) {
 				if (is_wares) {
-					game.send_player_command(*new Widelands::CmdSetWareTargetQuantity(
+					game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
 					   game.get_gametime(), player_->player_number(), serial_, index,
 					   tq.permanent + amount));
 				} else {
-					game.send_player_command(*new Widelands::CmdSetWorkerTargetQuantity(
+					game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
 					   game.get_gametime(), player_->player_number(), serial_, index,
 					   tq.permanent + amount));
 				}
@@ -209,10 +209,10 @@
 	for (const Widelands::DescriptionIndex& index : items) {
 		if (display_.ware_selected(index)) {
 			if (is_wares) {
-				game.send_player_command(*new Widelands::CmdResetWareTargetQuantity(
+				game.send_player_command(new Widelands::CmdResetWareTargetQuantity(
 				   game.get_gametime(), player_->player_number(), serial_, index));
 			} else {
-				game.send_player_command(*new Widelands::CmdResetWorkerTargetQuantity(
+				game.send_player_command(new Widelands::CmdResetWorkerTargetQuantity(
 				   game.get_gametime(), player_->player_number(), serial_, index));
 			}
 		}

=== modified file 'src/wui/game_message_menu.cc'
--- src/wui/game_message_menu.cc	2019-04-18 16:50:35 +0000
+++ src/wui/game_message_menu.cc	2019-05-01 07:38:30 +0000
@@ -336,7 +336,7 @@
 			//  Maybe the message was removed since think?
 			if (message->status() == Message::Status::kNew) {
 				Widelands::Game& game = iplayer().game();
-				game.send_player_command(*new Widelands::CmdMessageSetStatusRead(
+				game.send_player_command(new Widelands::CmdMessageSetStatusRead(
 				   game.get_gametime(), player.player_number(), id));
 			}
 			centerviewbtn_->set_enabled(message->position());
@@ -439,12 +439,12 @@
 		switch (mode) {
 		case Inbox:
 			// Archive highlighted message
-			game.send_player_command(*new Widelands::CmdMessageSetStatusArchived(
+			game.send_player_command(new Widelands::CmdMessageSetStatusArchived(
 			   game.get_gametime(), plnum, MessageId(selected_record)));
 			break;
 		case Archive:
 			// Restore highlighted message
-			game.send_player_command(*new Widelands::CmdMessageSetStatusRead(
+			game.send_player_command(new Widelands::CmdMessageSetStatusRead(
 			   game.get_gametime(), plnum, MessageId(selected_record)));
 			break;
 		}

=== modified file 'src/wui/warehousewindow.cc'
--- src/wui/warehousewindow.cc	2019-02-23 11:00:49 +0000
+++ src/wui/warehousewindow.cc	2019-05-01 07:38:30 +0000
@@ -160,7 +160,7 @@
 
 		for (const Widelands::DescriptionIndex& index : indices) {
 			if (display_.ware_selected(index)) {
-				gb_.game().send_player_command(*new Widelands::CmdSetStockPolicy(
+				gb_.game().send_player_command(new Widelands::CmdSetStockPolicy(
 				   gb_.game().get_gametime(), wh_.owner().player_number(), wh_, is_workers, index,
 				   newpolicy));
 			}


References