Skip to content

P2P LAN online multiplayer using UDP #3285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ if(NOT EMSCRIPTEN)
target_link_libraries(supertux2_lib PUBLIC LibCurl)
endif()

target_link_libraries(supertux2_lib PUBLIC pthread)

if(HAVE_OPENGL)
target_link_libraries(supertux2_lib PUBLIC LibOpenGL)
endif()
Expand Down
20 changes: 19 additions & 1 deletion src/control/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ std::optional<Control> Control_from_string(const std::string& text)

Controller::Controller():
m_touchscreen(false),
m_jump_key_pressed(false)
m_jump_key_pressed(false),
m_last_input("2")
{
reset();
}
Expand All @@ -86,6 +87,7 @@ Controller::reset()
}
m_touchscreen = false;
m_jump_key_pressed = false;
m_last_input = "2";
}

void
Expand Down Expand Up @@ -129,7 +131,23 @@ Controller::update()
{
for (int i = 0; i < static_cast<int>(Control::CONTROLCOUNT); ++i) {
m_old_controls[i] = m_controls[i];

if (m_controls[i] && m_last_input.find(std::to_string(i)) == std::string::npos) {
m_last_input += ":" + std::to_string(i);
}
}
}

void
Controller::add_last_input(const std::string& input)
{
m_last_input += input;
}

void
Controller::clear_last_input()
{
m_last_input= "2";
}

/* EOF */
8 changes: 8 additions & 0 deletions src/control/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class Controller

void set_jump_key_with_up(bool value);

inline std::string get_last_input() const { return m_last_input; }

void clear_last_input();

/** Touchscreen flag is set by MobileController, cleared when starting the level */
inline void set_touchscreen(bool value) { m_touchscreen = value; }

Expand Down Expand Up @@ -95,6 +99,8 @@ class Controller
/** returns true if the controller event has been generated by touchscreen */
inline bool is_touchscreen() const { return m_touchscreen; }

void add_last_input(const std::string& input);

protected:
/** current control status */
bool m_controls[static_cast<int>(Control::CONTROLCOUNT)];
Expand All @@ -108,6 +114,8 @@ class Controller
/** the jump key is pressed for this controller */
bool m_jump_key_pressed;

std::string m_last_input;

private:
Controller(const Controller&) = delete;
Controller& operator=(const Controller&) = delete;
Expand Down
6 changes: 3 additions & 3 deletions src/control/game_controller_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ GameControllerManager::on_controller_added(int joystick_index)
int id = m_parent->get_num_users();
for (int i = 0; i < m_parent->get_num_users(); i++)
{
if (!m_parent->has_corresponsing_controller(i) && !m_parent->m_uses_keyboard[i])
if (!m_parent->has_corresponsing_controller(i) && (!m_parent->m_uses_keyboard[i] && !m_parent->m_uses_online_controller[i]))
{
id = i;
break;
Expand Down Expand Up @@ -271,8 +271,8 @@ GameControllerManager::on_controller_removed(int instance_id)
m_game_controllers.erase(it);

if (m_parent->m_use_game_controller && g_config->multiplayer_auto_manage_players
&& deleted_player_id != 0 && !m_parent->m_uses_keyboard[deleted_player_id] &&
GameSession::current())
&& deleted_player_id != 0 && (!m_parent->m_uses_keyboard[deleted_player_id] &&
!m_parent->m_uses_online_controller[deleted_player_id]) && GameSession::current())
{
GameSession::current()->on_player_removed(deleted_player_id);
}
Expand Down
14 changes: 13 additions & 1 deletion src/control/input_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@

#include "control/input_manager.hpp"

#include "control/controller.hpp"
#include "control/game_controller_manager.hpp"
#include "control/joystick_config.hpp"
#include "control/joystick_manager.hpp"
#include "control/keyboard_manager.hpp"
#include "control/online_controller.hpp"
#include "util/log.hpp"

static constexpr int MAX_PLAYERS = 4;
Expand All @@ -32,7 +34,8 @@ InputManager::InputManager(KeyboardConfig& keyboard_config,
keyboard_manager(new KeyboardManager(this, keyboard_config)),
joystick_manager(new JoystickManager(this, joystick_config)),
game_controller_manager(new GameControllerManager(this)),
m_uses_keyboard()
m_uses_keyboard(),
m_uses_online_controller()
{
m_controllers.push_back(std::make_unique<Controller>());
}
Expand Down Expand Up @@ -153,6 +156,15 @@ InputManager::push_user()
m_controllers.push_back(std::make_unique<Controller>());
}

void
InputManager::push_online_user()
{
if (!can_add_user())
return;

m_controllers.push_back(std::make_unique<OnlineController>());
}

void
InputManager::pop_user()
{
Expand Down
3 changes: 3 additions & 0 deletions src/control/input_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class InputManager final : public Currenton<InputManager>
inline int get_num_users() const { return static_cast<int>(m_controllers.size()); }
bool can_add_user() const;
void push_user();
void push_online_user();
void pop_user();

void on_player_removed(int player_id);
Expand All @@ -78,6 +79,8 @@ class InputManager final : public Currenton<InputManager>

/** True if the given player is on the keyboard and plays without a controller */
std::unordered_map<int, bool> m_uses_keyboard;

std::unordered_map<int, bool> m_uses_online_controller;

private:
InputManager(const InputManager&) = delete;
Expand Down
4 changes: 2 additions & 2 deletions src/control/joystick_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ JoystickManager::on_joystick_added(int joystick_index)
int id = parent->get_num_users();
for (int i = 0; i < parent->get_num_users(); i++)
{
if (!parent->has_corresponsing_controller(i) && !parent->m_uses_keyboard[i])
if (!parent->has_corresponsing_controller(i) && (!parent->m_uses_keyboard[i] && !parent->m_uses_online_controller[i]))
{
id = i;
break;
Expand Down Expand Up @@ -123,7 +123,7 @@ JoystickManager::on_joystick_removed(int instance_id)
joysticks.erase(it);

if (!parent->m_use_game_controller && g_config->multiplayer_auto_manage_players
&& deleted_player_id != 0 && !parent->m_uses_keyboard[deleted_player_id] &&
&& deleted_player_id != 0 && (!parent->m_uses_keyboard[deleted_player_id] && !parent->m_uses_online_controller[deleted_player_id]) &&
GameSession::current())
{
GameSession::current()->on_player_removed(deleted_player_id);
Expand Down
51 changes: 51 additions & 0 deletions src/control/online_controller.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SuperTux
// Copyright (C) 2025 Martim Ferreira <[email protected]>
// 2025 Gonçalo Rocha <[email protected]>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include "control/online_controller.hpp"

OnlineController::OnlineController()
{
}

OnlineController::~OnlineController()
{
}

void
OnlineController::press(Control c)
{
m_controls[static_cast<int>(c)] = true;
m_last_input += ":" + std::to_string(static_cast<int>(c));
}

void
OnlineController::release(Control c)
{
m_controls[static_cast<int>(c)] = false;
}

void
OnlineController::clear_controls()
{
Controller::update();

for (int i = 0; i < static_cast<int>(Control::CONTROLCOUNT); ++i) {
m_controls[i] = false;
}
}

/* EOF */
46 changes: 46 additions & 0 deletions src/control/online_controller.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SuperTux
// Copyright (C) 2025 Martim Ferreira <[email protected]>
// 2025 Gonçalo Rocha <[email protected]>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#ifndef HEADER_SUPERTUX_CONTROL_ONLINE_CONTROLLER_HPP
#define HEADER_SUPERTUX_CONTROL_ONLINE_CONTROLLER_HPP

#include "control/controller.hpp"

class OnlineController final : public Controller
{
public:
OnlineController();
~OnlineController() override;

void press(Control c);

void release(Control c);

virtual void update() override
{
}

void clear_controls();

private:
OnlineController(const OnlineController&) = delete;
OnlineController& operator=(const OnlineController&) = delete;
};

#endif

/* EOF */
4 changes: 4 additions & 0 deletions src/object/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,10 @@ class Player final : public MovingObject

void stop_rolling(bool violent = true);

inline void set_player_position(const Vector& vector) { set_pos_reset(vector); }

inline Vector get_player_position() const { return get_pos(); }

private:
int m_id;
std::unique_ptr<UID> m_target; /**< (Multiplayer) If not null, then the player does not exist in game and is offering the player to spawn at that player's position */
Expand Down
20 changes: 19 additions & 1 deletion src/supertux/game_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@
#include "supertux/constants.hpp"
#include "supertux/fadetoblack.hpp"
#include "supertux/gameconfig.hpp"
#include "supertux/game_manager.hpp"
#include "supertux/globals.hpp"
#include "supertux/level.hpp"
#include "supertux/level_parser.hpp"
#include "supertux/levelintro.hpp"
#include "supertux/levelset_screen.hpp"
#include "supertux/menu/menu_storage.hpp"
#include "supertux/savegame.hpp"
#include "supertux/screen_manager.hpp"
#include "supertux/sector.hpp"
#include "supertux/shrinkfade.hpp"
#include "util/file_system.hpp"
#include "video/compositor.hpp"
Expand Down Expand Up @@ -822,6 +823,23 @@ GameSession::get_working_directory() const
return FileSystem::dirname(m_levelfile);
}

std::string
GameSession::get_current_world() const
{
const std::string& level_path = get_level_file();
const std::string prefix = "levels/";
size_t start = level_path.find(prefix);
if (start != std::string::npos)
{
start += prefix.length();
size_t end = level_path.find('/', start);
if (end != std::string::npos) {
return level_path.substr(0, end);
}
}
return "";
}

void
GameSession::start_sequence(Player* caller, Sequence seq, const SequenceData* data)
{
Expand Down
6 changes: 6 additions & 0 deletions src/supertux/game_session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "supertux/game_object.hpp"
#include "supertux/player_status.hpp"
#include "supertux/screen_fade.hpp"
#include "supertux/sector.hpp"
#include "supertux/sequence.hpp"
#include "supertux/timer.hpp"
#include "video/surface_ptr.hpp"
Expand Down Expand Up @@ -99,6 +100,7 @@ class GameSession final : public Screen,
void on_player_added(int id);
bool on_player_removed(int id);

inline Player* get_player(int id) const { return m_currentsector->get_player(id); }
void set_start_point(const std::string& sector, const std::string& spawnpoint);
void set_start_pos(const std::string& sector, const Vector& pos);
inline void set_respawn_point(const std::string& sector, const std::string& spawnpoint)
Expand All @@ -118,6 +120,8 @@ class GameSession final : public Screen,

inline Sector& get_current_sector() const { return *m_currentsector; }
inline Level& get_current_level() const { return *m_level; }
std::string get_current_world() const;


void start_sequence(Player* caller, Sequence seq, const SequenceData* data = nullptr);
void set_target_timer_paused(bool paused);
Expand All @@ -140,6 +144,8 @@ class GameSession final : public Screen,

void set_scheduler(SquirrelScheduler& new_scheduler);

inline bool is_playing_in_level() const { return m_currentsector != nullptr && m_level != nullptr && m_active && !m_game_pause && !m_end_sequence; }

private:
void check_end_conditions();

Expand Down
6 changes: 6 additions & 0 deletions src/supertux/gameconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ Config::Config() :
editor_show_deprecated_tiles(false),
multiplayer_auto_manage_players(true),
multiplayer_multibind(false),
is_joining(false),
is_hosting(false),
left_game(false),
go_to_level(false),
world(""),
level(""),
#if SDL_VERSION_ATLEAST(2, 0, 9)
multiplayer_buzz_controllers(true),
#else
Expand Down
7 changes: 7 additions & 0 deletions src/supertux/gameconfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ class Config final
bool multiplayer_multibind;
bool multiplayer_buzz_controllers;

bool is_joining;
bool is_hosting;

bool left_game;
bool go_to_level;
std::string world;
std::string level;
std::string repository_url;

bool is_christmas() const;
Expand Down
Loading
Loading