summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--externals/CMakeLists.txt4
m---------externals/enet0
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/citra_qt/CMakeLists.txt2
-rw-r--r--src/citra_qt/bootmanager.cpp3
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/logging/text_formatter.cpp1
-rw-r--r--src/core/memory.cpp25
-rw-r--r--src/network/CMakeLists.txt16
-rw-r--r--src/network/network.cpp50
-rw-r--r--src/network/network.h25
-rw-r--r--src/network/room.cpp60
-rw-r--r--src/network/room.h60
-rw-r--r--src/network/room_member.cpp74
-rw-r--r--src/network/room_member.h65
17 files changed, 384 insertions, 7 deletions
diff --git a/.gitmodules b/.gitmodules
index 36caa59f8..ac0df914d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -25,3 +25,6 @@
[submodule "fmt"]
path = externals/fmt
url = https://github.com/fmtlib/fmt.git
+[submodule "externals/enet"]
+ path = externals/enet
+ url = https://github.com/lsalzman/enet
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 02e02350c..cc47166fc 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -48,3 +48,7 @@ if (ARCHITECTURE_x86_64)
target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
endif()
+
+# ENet
+add_subdirectory(enet)
+target_include_directories(enet INTERFACE ./enet/include)
diff --git a/externals/enet b/externals/enet
new file mode 160000
+Subproject 9d9ba122d4818f7ae1aef2197933ac696edb233
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a45439481..655bd83aa 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(video_core)
add_subdirectory(audio_core)
+add_subdirectory(network)
add_subdirectory(input_common)
add_subdirectory(tests)
if (ENABLE_SDL2)
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 4841cbf05..9572d3e28 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -91,7 +91,7 @@ if (APPLE)
else()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
endif()
-target_link_libraries(citra-qt PRIVATE audio_core common core input_common video_core)
+target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets)
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index a8a4aed8b..30554890f 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -17,6 +17,7 @@
#include "core/settings.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
+#include "network/network.h"
EmuThread::EmuThread(GRenderWindow* render_window)
: exec_step(false), running(false), stop_run(false), render_window(render_window) {}
@@ -110,10 +111,12 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
setWindowTitle(QString::fromStdString(window_title));
InputCommon::Init();
+ Network::Init();
}
GRenderWindow::~GRenderWindow() {
InputCommon::Shutdown();
+ Network::Shutdown();
}
void GRenderWindow::moveContext() {
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 42f6a9918..0e4b85a76 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -72,6 +72,7 @@ namespace Log {
SUB(Audio, DSP) \
SUB(Audio, Sink) \
CLS(Input) \
+ CLS(Network) \
CLS(Loader)
// GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 1b905f66c..8f13b80b3 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -90,6 +90,7 @@ enum class Class : ClassType {
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
Input, ///< Input emulation
+ Network, ///< Network emulation
Count ///< Total number of logging classes
};
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index 9d423766f..f71e748d1 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -6,7 +6,6 @@
#include <cstdio>
#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index b8438e490..9024f4922 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -139,7 +139,12 @@ void UnmapRegion(VAddr base, u32 size) {
static u8* GetPointerFromVMA(VAddr vaddr) {
u8* direct_pointer = nullptr;
- auto& vma = Kernel::g_current_process->vm_manager.FindVMA(vaddr)->second;
+ auto& vm_manager = Kernel::g_current_process->vm_manager;
+
+ auto it = vm_manager.FindVMA(vaddr);
+ ASSERT(it != vm_manager.vma_map.end());
+
+ auto& vma = it->second;
switch (vma.type) {
case Kernel::VMAType::AllocatedMemoryBlock:
direct_pointer = vma.backing_block->data() + vma.offset;
@@ -147,6 +152,8 @@ static u8* GetPointerFromVMA(VAddr vaddr) {
case Kernel::VMAType::BackingMemory:
direct_pointer = vma.backing_memory;
break;
+ case Kernel::VMAType::Free:
+ return nullptr;
default:
UNREACHABLE();
}
@@ -341,11 +348,19 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
if (res_count == 0) {
PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (page_type) {
- case PageType::RasterizerCachedMemory:
- page_type = PageType::Memory;
- current_page_table->pointers[vaddr >> PAGE_BITS] =
- GetPointerFromVMA(vaddr & ~PAGE_MASK);
+ case PageType::RasterizerCachedMemory: {
+ u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
+ if (pointer == nullptr) {
+ // It's possible that this function has called been while updating the pagetable
+ // after unmapping a VMA. In that case the underlying VMA will no longer exist,
+ // and we should just leave the pagetable entry blank.
+ page_type = PageType::Unmapped;
+ } else {
+ page_type = PageType::Memory;
+ current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
+ }
break;
+ }
case PageType::RasterizerCachedSpecial:
page_type = PageType::Special;
break;
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
new file mode 100644
index 000000000..aeabe430e
--- /dev/null
+++ b/src/network/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SRCS
+ network.cpp
+ room.cpp
+ room_member.cpp
+ )
+
+set(HEADERS
+ network.h
+ room.h
+ room_member.h
+ )
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(network STATIC ${SRCS} ${HEADERS})
+target_link_libraries(network PRIVATE common enet)
diff --git a/src/network/network.cpp b/src/network/network.cpp
new file mode 100644
index 000000000..51b5d6a9f
--- /dev/null
+++ b/src/network/network.cpp
@@ -0,0 +1,50 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "enet/enet.h"
+#include "network/network.h"
+
+namespace Network {
+
+static std::shared_ptr<RoomMember> g_room_member; ///< RoomMember (Client) for network games
+static std::shared_ptr<Room> g_room; ///< Room (Server) for network games
+// TODO(B3N30): Put these globals into a networking class
+
+bool Init() {
+ if (enet_initialize() != 0) {
+ LOG_ERROR(Network, "Error initalizing ENet");
+ return false;
+ }
+ g_room = std::make_shared<Room>();
+ g_room_member = std::make_shared<RoomMember>();
+ LOG_DEBUG(Network, "initialized OK");
+ return true;
+}
+
+std::weak_ptr<Room> GetRoom() {
+ return g_room;
+}
+
+std::weak_ptr<RoomMember> GetRoomMember() {
+ return g_room_member;
+}
+
+void Shutdown() {
+ if (g_room_member) {
+ if (g_room_member->IsConnected())
+ g_room_member->Leave();
+ g_room_member.reset();
+ }
+ if (g_room) {
+ if (g_room->GetState() == Room::State::Open)
+ g_room->Destroy();
+ g_room.reset();
+ }
+ enet_deinitialize();
+ LOG_DEBUG(Network, "shutdown OK");
+}
+
+} // namespace Network
diff --git a/src/network/network.h b/src/network/network.h
new file mode 100644
index 000000000..6d002d693
--- /dev/null
+++ b/src/network/network.h
@@ -0,0 +1,25 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "network/room.h"
+#include "network/room_member.h"
+
+namespace Network {
+
+/// Initializes and registers the network device, the room, and the room member.
+bool Init();
+
+/// Returns a pointer to the room handle
+std::weak_ptr<Room> GetRoom();
+
+/// Returns a pointer to the room member handle
+std::weak_ptr<RoomMember> GetRoomMember();
+
+/// Unregisters the network device, the room, and the room member and shut them down.
+void Shutdown();
+
+} // namespace Network
diff --git a/src/network/room.cpp b/src/network/room.cpp
new file mode 100644
index 000000000..48de2f5cb
--- /dev/null
+++ b/src/network/room.cpp
@@ -0,0 +1,60 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "enet/enet.h"
+#include "network/room.h"
+
+namespace Network {
+
+/// Maximum number of concurrent connections allowed to this room.
+static constexpr u32 MaxConcurrentConnections = 10;
+
+class Room::RoomImpl {
+public:
+ ENetHost* server = nullptr; ///< Network interface.
+
+ std::atomic<State> state{State::Closed}; ///< Current state of the room.
+ RoomInformation room_information; ///< Information about this room.
+};
+
+Room::Room() : room_impl{std::make_unique<RoomImpl>()} {}
+
+Room::~Room() = default;
+
+void Room::Create(const std::string& name, const std::string& server_address, u16 server_port) {
+ ENetAddress address;
+ address.host = ENET_HOST_ANY;
+ enet_address_set_host(&address, server_address.c_str());
+ address.port = server_port;
+
+ room_impl->server = enet_host_create(&address, MaxConcurrentConnections, NumChannels, 0, 0);
+ // TODO(B3N30): Allow specifying the maximum number of concurrent connections.
+ room_impl->state = State::Open;
+
+ room_impl->room_information.name = name;
+ room_impl->room_information.member_slots = MaxConcurrentConnections;
+
+ // TODO(B3N30): Start the receiving thread
+}
+
+Room::State Room::GetState() const {
+ return room_impl->state;
+}
+
+const RoomInformation& Room::GetRoomInformation() const {
+ return room_impl->room_information;
+}
+
+void Room::Destroy() {
+ room_impl->state = State::Closed;
+ // TODO(B3n30): Join the receiving thread
+
+ if (room_impl->server) {
+ enet_host_destroy(room_impl->server);
+ }
+ room_impl->room_information = {};
+ room_impl->server = nullptr;
+}
+
+} // namespace Network
diff --git a/src/network/room.h b/src/network/room.h
new file mode 100644
index 000000000..70c64d5f1
--- /dev/null
+++ b/src/network/room.h
@@ -0,0 +1,60 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+
+namespace Network {
+
+constexpr u16 DefaultRoomPort = 1234;
+constexpr size_t NumChannels = 1; // Number of channels used for the connection
+
+struct RoomInformation {
+ std::string name; ///< Name of the server
+ u32 member_slots; ///< Maximum number of members in this room
+};
+
+/// This is what a server [person creating a server] would use.
+class Room final {
+public:
+ enum class State : u8 {
+ Open, ///< The room is open and ready to accept connections.
+ Closed, ///< The room is not opened and can not accept connections.
+ };
+
+ Room();
+ ~Room();
+
+ /**
+ * Gets the current state of the room.
+ */
+ State GetState() const;
+
+ /**
+ * Gets the room information of the room.
+ */
+ const RoomInformation& GetRoomInformation() const;
+
+ /**
+ * Creates the socket for this room. Will bind to default address if
+ * server is empty string.
+ */
+ void Create(const std::string& name, const std::string& server = "",
+ u16 server_port = DefaultRoomPort);
+
+ /**
+ * Destroys the socket
+ */
+ void Destroy();
+
+private:
+ class RoomImpl;
+ std::unique_ptr<RoomImpl> room_impl;
+};
+
+} // namespace Network
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
new file mode 100644
index 000000000..c87f009f4
--- /dev/null
+++ b/src/network/room_member.cpp
@@ -0,0 +1,74 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "enet/enet.h"
+#include "network/room_member.h"
+
+namespace Network {
+
+constexpr u32 ConnectionTimeoutMs = 5000;
+
+class RoomMember::RoomMemberImpl {
+public:
+ ENetHost* client = nullptr; ///< ENet network interface.
+ ENetPeer* server = nullptr; ///< The server peer the client is connected to
+
+ std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
+
+ std::string nickname; ///< The nickname of this member.
+};
+
+RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {
+ room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0);
+ ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client");
+}
+
+RoomMember::~RoomMember() {
+ ASSERT_MSG(!IsConnected(), "RoomMember is being destroyed while connected");
+ enet_host_destroy(room_member_impl->client);
+}
+
+RoomMember::State RoomMember::GetState() const {
+ return room_member_impl->state;
+}
+
+void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port,
+ u16 client_port) {
+ ENetAddress address{};
+ enet_address_set_host(&address, server_addr);
+ address.port = server_port;
+
+ room_member_impl->server =
+ enet_host_connect(room_member_impl->client, &address, NumChannels, 0);
+
+ if (!room_member_impl->server) {
+ room_member_impl->state = State::Error;
+ return;
+ }
+
+ ENetEvent event{};
+ int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs);
+ if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
+ room_member_impl->nickname = nick;
+ room_member_impl->state = State::Joining;
+ // TODO(B3N30): Send a join request with the nickname to the server
+ // TODO(B3N30): Start the receive thread
+ } else {
+ room_member_impl->state = State::CouldNotConnect;
+ }
+}
+
+bool RoomMember::IsConnected() const {
+ return room_member_impl->state == State::Joining || room_member_impl->state == State::Joined;
+}
+
+void RoomMember::Leave() {
+ enet_peer_disconnect(room_member_impl->server, 0);
+ room_member_impl->state = State::Idle;
+ // TODO(B3N30): Close the receive thread
+ enet_peer_reset(room_member_impl->server);
+}
+
+} // namespace Network
diff --git a/src/network/room_member.h b/src/network/room_member.h
new file mode 100644
index 000000000..177622b69
--- /dev/null
+++ b/src/network/room_member.h
@@ -0,0 +1,65 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+#include "network/room.h"
+
+namespace Network {
+
+/**
+ * This is what a client [person joining a server] would use.
+ * It also has to be used if you host a game yourself (You'd create both, a Room and a
+ * RoomMembership for yourself)
+ */
+class RoomMember final {
+public:
+ enum class State : u8 {
+ Idle, ///< Default state
+ Error, ///< Some error [permissions to network device missing or something]
+ Joining, ///< The client is attempting to join a room.
+ Joined, ///< The client is connected to the room and is ready to send/receive packets.
+ LostConnection, ///< Connection closed
+
+ // Reasons why connection was rejected
+ NameCollision, ///< Somebody is already using this name
+ MacCollision, ///< Somebody is already using that mac-address
+ CouldNotConnect ///< The room is not responding to a connection attempt
+ };
+
+ RoomMember();
+ ~RoomMember();
+
+ /**
+ * Returns the status of our connection to the room.
+ */
+ State GetState() const;
+
+ /**
+ * Returns whether we're connected to a server or not.
+ */
+ bool IsConnected() const;
+
+ /**
+ * Attempts to join a room at the specified address and port, using the specified nickname.
+ * This may fail if the username is already taken.
+ */
+ void Join(const std::string& nickname, const char* server_addr = "127.0.0.1",
+ const u16 serverPort = DefaultRoomPort, const u16 clientPort = 0);
+
+ /**
+ * Leaves the current room.
+ */
+ void Leave();
+
+private:
+ class RoomMemberImpl;
+ std::unique_ptr<RoomMemberImpl> room_member_impl;
+};
+
+} // namespace Network