Merge lp:~widelands-dev/widelands/net-boost-asio into lp:widelands
- net-boost-asio
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 8377 | ||||||||
Proposed branch: | lp:~widelands-dev/widelands/net-boost-asio | ||||||||
Merge into: | lp:widelands | ||||||||
Diff against target: |
2058 lines (+887/-493) 26 files modified
CMakeLists.txt (+4/-3) CREDITS (+0/-1) appveyor.yml (+1/-1) cmake/Modules/FindSDL2_net.cmake (+0/-88) cmake/WlFunctions.cmake (+0/-6) src/network/CMakeLists.txt (+0/-2) src/network/gameclient.cc (+4/-9) src/network/gameclient.h (+5/-7) src/network/gamehost.cc (+0/-1) src/network/internet_gaming.cc (+10/-5) src/network/internet_gaming.h (+1/-6) src/network/netclient.cc (+57/-37) src/network/netclient.h (+26/-21) src/network/nethost.cc (+142/-42) src/network/nethost.h (+46/-22) src/network/network.cc (+55/-1) src/network/network.h (+55/-1) src/network/network_lan_promotion.cc (+348/-107) src/network/network_lan_promotion.h (+101/-23) src/network/network_system.h (+0/-59) src/ui_fsmenu/internet_lobby.cc (+12/-11) src/ui_fsmenu/netsetup_lan.cc (+8/-11) src/ui_fsmenu/netsetup_lan.h (+3/-7) src/wlapplication.cc (+9/-20) utils/macos/build_app.sh (+0/-1) utils/win32/innosetup/Widelands.iss (+0/-1) |
||||||||
To merge this branch: | bzr merge lp:~widelands-dev/widelands/net-boost-asio | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Klaus Halfmann | compile, test, small review | Approve | |
Tino | Needs Resubmitting | ||
Notabilis | Needs Resubmitting | ||
Review via email: mp+324364@code.launchpad.net |
Commit message
Replaced SDL-Net with Boost.Asio.
Added IPv6 support for non-metaserver games.
Description of the change
Replaced the SDL-Net network code with Boost.Asio. This removes SDL-Net as a required library, unfortunately we now require boost-system.
Also added support for IPv6. As of now, this only works for games in the local network, since the metaserver host does not support IPv6 at the moment.
Warning: This branch is not yet ready for merge. I removed the sdl-net library but I don't know / can't test how to add the boost requirements to the build process for Windows and MacOS. For Linux I only had to add the option to link with boost-system in CMakeList.txt. It would be great if someone could test the other operating systems and, if needed, add the required linking for them. The commit where I removed the sdl-net references is:
http://
bunnybot (widelandsofficial) wrote : | # |
GunChleoc (gunchleoc) wrote : | # |
I have done a code review anyway. Since I don't know that much about networking, it's for code style only.
I have also given it a quick spin and found that IPv4 is preferred over IPv6, I think it should be the other way around if possible, since v6 is the more modern protocol.
Notabilis (notabilis27) wrote : | # |
Thanks for the review! I included all your suggestions, even though the "prefer IPv6" part is somewhere else in the code.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2236. State: passed. Details: https:/
Appveyor build 2071. State: failed. Details: https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2241. State: passed. Details: https:/
Appveyor build 2076. State: success. Details: https:/
Notabilis (notabilis27) wrote : | # |
Seems like its building on Windows (or at least on Appveyor) now. A confirmation and a short test would be appreciated, though. Also, building and testing on MacOS is still missing.
Klaus Halfmann (klaus-halfmann) wrote : | # |
tried to compile this an it failed me with:
net-boost-
'ifa_ifu' in 'ifaddrs'
s = getnameinfo(
So Do _you_ use some implementation detail, or do I have to chnange something?
Can you give my some link to what this Boost.Asio is?
Ill as the usal suspects in the internet meanwhile.
Klaus Halfmann (klaus-halfmann) wrote : | # |
OK checked some things,
I found Asio 1.10.6 / Boost 1.58 as part of the boost library
provided by MacPorts (https:/
most recent is Asio 1.10.9 / Boost 1.64
I assume you are on Ubuntu, so I assume you are somehwere in the middle of these versions.
Can anybody tell use the windows boost versions we use?
Klaus Halfmann (klaus-halfmann) wrote : | # |
OK you use some struct ifaddrs from
$FreeBSD: src/include/
As OSX is bases on Freebsd no surprise here.
man getifaddr tells me
The ifaddrs structure contains at least the following entries:
struct ifaddrs *ifa_next; /* Pointer to next struct */
char *ifa_name; /* Interface name */
u_int ifa_flags; /* Interface flags */
struct sockaddr *ifa_addr; /* Interface address */
struct sockaddr *ifa_netmask; /* Interface netmask */
struct sockaddr *ifa_dstaddr; /* P2P interface destination */
void *ifa_data; /* Address specific data */
...
Note that as a convenience, ifa_broadaddr is defined by a compiler
#define directive to be the same as ifa_dstaddr.
On linux this is:
struct ifaddrs *ifa_next; /* Next item in list */
char *ifa_name; /* Name of interface */
unsigned int ifa_flags; /* Flags from SIOCGIFFLAGS */
struct sockaddr *ifa_addr; /* Address of interface */
struct sockaddr *ifa_netmask; /* Netmask of interface */
union {
struct sockaddr *ifu_broadaddr;
struct sockaddr *ifu_dstaddr;
} ifa_ifu;
#define ifa_broadaddr ifa_ifu.
#define ifa_dstaddr ifa_ifu.ifu_dstaddr
void *ifa_data; /* Address-specific data */
No ifa_ifu here, but as of the Note for BSD this should be ifa_broadaddr ...
Ok, this did the trick, will cehck in this chanage and some struct/class
confusions my compiler complains about.
Notabilis: will you merge with trunk as Gun has commited some major change today?
GunChleoc (gunchleoc) wrote : | # |
> I found Asio 1.10.6 / Boost 1.58 as part of the boost library
> provided by MacPorts (https:/
>
> most recent is Asio 1.10.9 / Boost 1.64
From CMakelists.txt:
find_package(Boost 1.48
COMPONENTS
unit_
regex
REQUIRED
system)
So, we require version 1.48 as our lowest boost version. If we need a
higher version for the ASIO stuff, we will need to change that line.
The lowest Ubuntu version that we support is Trusty (14.04), which
already comes with Boost version 1.54. Travis is also on Boost 1.54
AppVeyor is pulling Boost version 1.63.
We should go over
http://
see if we need to require a higher Boost version.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Trying an itenratgame now gives me:
InternetGaming: Client opened a game with the name BugHasi.
[NetHost]: Opening a listening IPv4 socket on TCP port 7396
[NetHost]: Opening a listening IPv6 socket on TCP port 7396
[LAN] Started an IPv6 socket on UDP port 7395
[LAN] Started an IPv4 socket on UDP port 7395
[LAN] Will broadcast to 192.168.254.255
[LAN] Will broadcast for IPv6
[LAN] Closing an IPv6 socket.
[NetHost]: Closing a listening IPv4 socket
[NetHost]: Closing a listening IPv6 socket
[LAN] Closing an IPv4 socket.
Warning: Failed to use the local network!
Widelands was unable to use the local network. Maybe some other process is already running a server on port 7394, 7395 or 7396 or your network setup is broken.
Any idea what this is about?
> netstat -a | fgrep 139
does not show me any such port. going to debug this now
Klaus Halfmann (klaus-halfmann) wrote : | # |
* I can connect to the metaserver, so networking basically works.
* With a second try I now died with:
Assertion failed: (state_ == OFFLINE), function login,
file /Users/
But I think this is already adressed in some other fix.
* Hmm, You use
d->promoter = new LanGamePromoter()
with a BroadcastAddr even for an internet game.
And this then fails here:
boost::
socket_
please fix:
* no Brodcast for a pure Internet game.
* in case one of IVP4 _or_ IPV6 broadcast can be used,
do not fail but log a warning only.
I will not try a local network game until this is fixed.
I assume it will fail in the same way.
Thanks for your work nonetheless, you may find me in the Lobby between
18:00 and 20:00 CET some times, so we may further discuss my findings
GunChleoc (gunchleoc) wrote : | # |
I ran a LAN game with Windows host and Linux client, no problem.
Will do more testing when the issues raised by Klaus have been addressed.
Notabilis (notabilis27) wrote : | # |
Thanks a lot for testing! Sorry, somehow missed this yesterday.
- Thanks for fixing the find-broadcast code, it compiles and works on my system, too.
- Trunk is merged, luckily without conflicts.
- I went through the boost history, seems like version 1.48 is good enough. A lot of fixes but no changes that should be relevant for us.
- Fixed the too early abort when only one broadcast failed. Does your system has an IPv6 (address)? Its strange that earlier the code detected IPv6 on your system but later on is unable to send using it.
- Whether to remove the broadcast for internet games is up to discussion. It was already in the code before I worked on it. Since it allows LAN clients to connect to our game I don't see any harm in it.
- Regarding the failed assertion: You connected to the metaserver, clicked "back" and connected again without restarting the game? Have there been any error messages before the assert? I am not sure whether this is fixed, when it happens again let me know.
Klaus Halfmann (klaus-halfmann) wrote : | # |
* Hello Notabalis: I will proceed as follows:
* Try "normal" network games with another instance as host and vice versa
* Try local network gaming (assuming its IPv4)
* Find out what wrong with IPv6 on this Mac, I have seen the error code
(I hope you log it), So i can ivestigate there.
* Either my Firewall needs tweaking
* Or IPv6 Broadcasting on Darvin/OSX is different
* or what else...
* Local Broadcasting
- Pollutes the local network with unneeded traffic
+ Allows local computers to connect not knowing the server IP
* it should not make normal network games fail :-)
* I think the Errror after a failed connect is already adressed in some other bug/branch
https:/
so maybe your merge fixed it.
Lest go compiling ....
Klaus Halfmann (klaus-halfmann) wrote : | # |
Basi Network gamig now works,
I get
[NetHost]: Opening a listening IPv6 socket on TCP port 7396
[LAN] Started an IPv6 socket on UDP port 7395
[LAN] Started an IPv4 socket on UDP port 7395
[LAN] Will broadcast to 192.168.254.255
[LAN] Will broadcast for IPv6
[LAN] Closing an IPv6 socket.
Wee need more loggin why the socket is closed, well.
Did some basic Testing with Gun,
-> but we should play perhpas 30 Minutes.
Connection to localhost form a second instance works.
Found the Broadcasted local Game, OK,
-> Will do som cross test vs. trunk next
looks god so far when IPv4 is ok this can go to trunk
we can work on IPv6 issues on some other branch.
Cann we do some testing this weekend?
Notabilis (notabilis27) wrote : | # |
Added log outputs for all errors, hopefully this will help finding out whats going on.
I have some dates tomorrow, but I will be in the lobby as much as possible. Sunday would work, too.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2257. State: failed. Details: https:/
Appveyor build 2092. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
* Hmm, here is the IPv6 Error:
[LAN] Started an IPv6 socket on UDP port 7394.
...
[LAN] Will broadcast for IPv6.
[LAN] Error when broadcasting on IPv6 socket, closing it: No route to host.
[LAN] Closing an IPv6 socket.
Must use debugger/consult docs what this is about.
Looks Broadcast adresses in IPv6 need some differetn aproach.
* Testing lcoal network (OSX)
server <- client
asio <- asio game is not visible in the local network browser (but is fast)
but can connect via localhost
old <- asio game is visible in the local network browser (but slow setting this up)
asio <- old game is visible in the local network browser
Now I am puzzeld an will need some debugging to find this.
(And we will need some Linux, OSX, Windows ... LAN Party for the final checks :-)
Notabilis please give some hint where to check.
As its is bsiaclly working I would like to see some > 30 min Internet game,
for stabilit, we then vould write seom bugs for our findings.
I will start the code review now, while wating for some victim in the lobby for testing :-)
Klaus Halfmann (klaus-halfmann) wrote : | # |
Travis has Problme with just CLANG_VERSION="4.0" BUILD_TYPE="Debug"
all others are ok?
Looks like some transient error, llvm.or had a bad time?
sudo apt-get install -qq --force-yes -y clang-4.0
E: Failed to fetch http://
Klaus Halfmann (klaus-halfmann) wrote : | # |
Did a review now,
* there is quite some duplicate code for 4/6 that should be avoided
* Think about a common errro handling, that will save a lot of code, like
if (hasError(ec)) { // implies logging
check the comments inline.
we still could get his in and improved this later.
Got some "visitors" from Libre Game Night, so my review is imcomplete sorry.
Can approve, and mabye fix the Issues later
GunChleoc (gunchleoc) wrote : | # |
Re Travis: Yes, that was just Travis unable to get a package. So, it doesn't count as an error for our code.
Notabilis (notabilis27) wrote : | # |
Thanks for the review, I will address most of it.
I commented some of your comments, on two points I disagree with you and would prefer the way it currently is. Feel free to insist if you think I am wrong.
Notabilis (notabilis27) wrote : | # |
Review is dealt with. The code looks prettier now, thanks.
I have to do some more reading about IPv6 and OSX. I fear we will need some platform specific code there. I will try to push something for testing in the next days.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Code looks much better, no more 4/6 duplicates.
Will now play a bit, again.
Notabilis (notabilis27) wrote : | # |
Here, I have something more to play with for you. ;-)
When I understand it right, with the current version you are always getting the log message "closing IPv6 socket" when entering the LAN lobby, right? With my newest push this message hopefully does not appear any longer. And if it works really good, two computers should be able to find their games over the LAN with IPv6.
When trying, make sure the __APPLE__ branch of the code is compiled. It should also print a comment on console when entering the LAN lobby. But if you don't have to better don't look at the code, its very ugly.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2260. State: failed. Details: https:/
Appveyor build 2095. State: failed. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
Codecheck from travis fails, but for some compilers?
grep '^[/_.a-
/home/travis/
/home/travis/
/home/travis/
/home/travis/
/home/travis/
/home/travis/
/home/travis/
/home/travis/
+echo 'You have codecheck warnings (see above) Please fix.'
You have codecheck warnings (see above) Please fix.
Notabilis (notabilis27) wrote : | # |
Travis is only complaining about my coding style, which I have to agree with. The current apple-test-code is totally ugly. ;-)
The failing builds are the debug builds, seems like release builds aren't doing codechecks.
When I understood it right, the new code worked on your system, or? If you want to try with only IPv6 enabled, add "close_
If the code is working, I will keep it in and clean it up.
GunChleoc (gunchleoc) wrote : | # |
Exactly, codecheck is only run with debug builds. So, it's failing all the debug builds, but not the release builds.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Will not have much time the next 3 weeks I guess.
I aon call duty for my job and the weekends are stuffed
with other thing I must care for.
Lets fix the ugly apple code and then lets get this in.
I like having smaller steps and the history of this branch
is long enought ...
sorry
GunChleoc (gunchleoc) wrote : | # |
Don't worry, RL always has to come first. Thanks for all this extensive testing!
Notabilis (notabilis27) wrote : | # |
Fine by me, I will try to do so.
Thanks a lot for all the testing, it was a great help!
Notabilis (notabilis27) wrote : | # |
So, code is cleaned up. A review of the new apple specific parts and maybe another short test, then it can go?
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2267. State: passed. Details: https:/
Appveyor build 2102. State: failed. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
Sounds like a plan - AppVeyor builds are broken though, so we still need to make Windows happy.
Tino (tino79) wrote : | # |
Windows.h includes winsock.h, which clashes with winsock2 and boost asio.
The additional define WIN32_LEAN_AND_MEAN avoids including winsock.h and some other headers.
Klaus Halfmann (klaus-halfmann) wrote : | # |
Oops, now this does not compile on OSX:
net-boost-
'
in the .h files this is:
#ifndef __APPLE__
/// Apple forces us to define which interface to broadcast through.
#endif // __APPLE__
-> need another #ifdef then...
Notabilis (notabilis27) wrote : | # |
Uh uh, my fault. This should have actually been an #ifdef. Is fixed now.
Klaus Halfmann (klaus-halfmann) wrote : | # |
After playing quite a qhile (> 1hour) I now got a crash:
Thread 0 Crashed:: Dispatch queue: com.apple.
0 widelands 0x000000010a7c2d9c boost::
1 widelands 0x000000010a7c03ef boost::
2 widelands 0x000000010a7bceb9 NetClient:
3 widelands 0x000000010a7bd609 NetClient:
4 widelands 0x000000010a7526c1 InternetGaming:
5 widelands 0x000000010a795f1c GameHost::run() + 9932 (gamehost.cc:749)
So the interal asio service object was broken :-(
should this be our long searched for bug in trunk?
Notabilis (notabilis27) wrote : | # |
See the positive side: You were able to compile the branch! ;-)
Seems like some kind of memory corruption though I am not sure where it is coming from. The line in GameHost indicates that the server crashed. What were you doing when it happened? Could it be that your network went offline (just a wild guess)?
Which bug in trunk do you mean? Random crashes or something network related? (The asio service does not exist in trunk but a similar problem could probably happen there, too.)
An unrelated question to whomever: I am a bit worried that Appveyor isn't saying anything. Is it just slow or does that mean that the code is still broken?
Tino (tino79) wrote : | # |
It seems the Bazaar-
SirVer (sirver) wrote : | # |
Sorry for that. Bunnybot did not restart after a computer reboot and I did not even notice the computer rebooted. Should work now again.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2272. State: passed. Details: https:/
Appveyor build 2107. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
One nit inline - no need to fix this.
We should get this in now, otherwise we will never get enouht testing coverage.
Notablilis: try pulling the cable at several stepts in the game,
maybe there is one error left, I am no 100% sure.
FAPP tthis is OK for me.
Notabilis (notabilis27) wrote : | # |
Thanks for the explanation and fixing Bunnybot! Good to see that it is working again.
I did a short test with two network interfaces on Linux. With the current code one packet is sent on both interfaces, so the kernel (or so) is already dealing with duplicating them. With the apple-code, two packets with the same contents are sent on both interfaces. So as long as it doesn't make any problems, I would keep the current code.
Removing/Adding the network cable did not change anything but some time later I also got the exception. The problem is/was that the metaserver-
But... is it my system or is trunk (and this branch) currently broken? When ingame the view is permanently scrolling down, as if the arrow-down key is pressed.
Notabilis (notabilis27) wrote : | # |
Pushed a fix for an assert(state_ == OFFLINE) bug which I encountered. The problem was that on crash of a hosted game the logic jumped back to the main menu without closing the connection to the metaserver. So when the player tries to connect again, he already is connected to the metaserver (and not offline).
To reproduce the bug:
- Login to the metaserver
- Cut your wifi cable
- Try to host a game. Should fail and jump you back into the main menu
- Try to connect to the metaserver again (I did this one without network)
- Game crashes due to the assert
Klaus Halfmann (klaus-halfmann) wrote : | # |
Ahh. As I have an untrustbale network cable I have seen this already.
You changes are fine with me.
GunChleoc (gunchleoc) wrote : | # |
> When ingame the view is permanently scrolling down, as if the arrow-down key is pressed.
I have never heard of that one.
Notabilis (notabilis27) wrote : | # |
Today it does not happen for me anymore. Really strange effect, no idea what happened there.
So, if no-one has any requests or suggestions left, lets merge this?
GunChleoc (gunchleoc) wrote : | # |
I'd like to give it a quick spin on Windows first, I just haven't gotten around to it yet.
GunChleoc (gunchleoc) wrote : | # |
I had another look at the code and added 9 small code style/English language nits. Fix as you see fit.
GunChleoc (gunchleoc) wrote : | # |
Testing on Windows was successful with LAN + internet game.
Feel free to merge any time - don't forget to set a commit message on top of this page first though.
Notabilis (notabilis27) wrote : | # |
Thanks for testing and the review. I fixed all your nits except the iterator. A foreach-loop would not work since I need it to get the key of the std::map and also since I am modifying the map while running through it. The temporary key-variable (for it->first) could be probably removed when I write code like "it++->first". But I at least prefer the better readable version with the variable.
I will give Bunnybot another chance to run, then this will go in.
GunChleoc (gunchleoc) wrote : | # |
Well, this should work:
for (auto& client: clients_) {
...
...
...
}
You don't need an explicit iterator to access the first and second element of the pair.
Notabilis (notabilis27) wrote : | # |
Oh, thanks for the hint! I wasn't aware that this loop allows access to the key. It does not solve the removal-problem, however. When I remove the client while in the loop the behavior of the rest of the loop will be undefined.
I could add a local std::set and store the to-be-removed ids in there, removing them when the loop is done. Do you prefer this to the iterator?
std::set to_remove;
for (auto& client: clients_) {
...
if (error) {
}
...
}
for (auto id: to_remove)
close(id);
GunChleoc (gunchleoc) wrote : | # |
Basically, that loop will give you the elements in the container, so you can do all the stuff with them that you would do if it was a simple variable.
I wasn't aware that close() will remove an element from the container. Best stay with the iterator after all then - adding a second loop + container would make the code unnecessarily complicated.
GunChleoc (gunchleoc) wrote : | # |
Let's get this in now. Thanks for the great change :)
@bunnybot merge
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2017-02-28 08:31:53 +0000 | |||
3 | +++ CMakeLists.txt 2017-06-07 18:50:40 +0000 | |||
4 | @@ -41,6 +41,7 @@ | |||
5 | 41 | 41 | ||
6 | 42 | if (WIN32) | 42 | if (WIN32) |
7 | 43 | set (Boost_USE_STATIC_LIBS ON) | 43 | set (Boost_USE_STATIC_LIBS ON) |
8 | 44 | link_libraries(wsock32 ws2_32) | ||
9 | 44 | else() | 45 | else() |
10 | 45 | set (Boost_USE_STATIC_LIBS OFF) | 46 | set (Boost_USE_STATIC_LIBS OFF) |
11 | 46 | endif() | 47 | endif() |
12 | @@ -50,7 +51,8 @@ | |||
13 | 50 | COMPONENTS | 51 | COMPONENTS |
14 | 51 | unit_test_framework | 52 | unit_test_framework |
15 | 52 | regex | 53 | regex |
17 | 53 | REQUIRED) | 54 | REQUIRED |
18 | 55 | system) | ||
19 | 54 | 56 | ||
20 | 55 | find_package (PythonInterp REQUIRED) | 57 | find_package (PythonInterp REQUIRED) |
21 | 56 | 58 | ||
22 | @@ -60,7 +62,6 @@ | |||
23 | 60 | find_package(SDL2 REQUIRED) | 62 | find_package(SDL2 REQUIRED) |
24 | 61 | find_package(SDL2_image REQUIRED) | 63 | find_package(SDL2_image REQUIRED) |
25 | 62 | find_package(SDL2_mixer REQUIRED) | 64 | find_package(SDL2_mixer REQUIRED) |
26 | 63 | find_package(SDL2_net REQUIRED) | ||
27 | 64 | find_package(SDL2_ttf REQUIRED) | 65 | find_package(SDL2_ttf REQUIRED) |
28 | 65 | find_package(ZLIB REQUIRED) | 66 | find_package(ZLIB REQUIRED) |
29 | 66 | find_package(ICU REQUIRED) | 67 | find_package(ICU REQUIRED) |
30 | @@ -181,7 +182,7 @@ | |||
31 | 181 | wl_add_flag(WL_COMPILE_DIAGNOSTICS "-Werror=uninitialized") | 182 | wl_add_flag(WL_COMPILE_DIAGNOSTICS "-Werror=uninitialized") |
32 | 182 | 183 | ||
33 | 183 | IF (WIN32) | 184 | IF (WIN32) |
35 | 184 | add_definitions(-DMINGW_HAS_SECURE_API) | 185 | add_definitions(-DMINGW_HAS_SECURE_API -DWIN32_LEAN_AND_MEAN) |
36 | 185 | if (CMAKE_SIZEOF_VOID_P EQUAL 4) | 186 | if (CMAKE_SIZEOF_VOID_P EQUAL 4) |
37 | 186 | set (CMAKE_EXE_LINKER_FLAGS "-Wl,--large-address-aware" CACHE STRING "Set by widelands CMakeLists.txt" FORCE) | 187 | set (CMAKE_EXE_LINKER_FLAGS "-Wl,--large-address-aware" CACHE STRING "Set by widelands CMakeLists.txt" FORCE) |
38 | 187 | message (STATUS "Enabled large address awareness on mingw32") | 188 | message (STATUS "Enabled large address awareness on mingw32") |
39 | 188 | 189 | ||
40 | === modified file 'CREDITS' | |||
41 | --- CREDITS 2014-10-13 15:04:50 +0000 | |||
42 | +++ CREDITS 2017-06-07 18:50:40 +0000 | |||
43 | @@ -10,7 +10,6 @@ | |||
44 | 10 | 10 | ||
45 | 11 | libSDL2 | 11 | libSDL2 |
46 | 12 | libSDL2_mixer | 12 | libSDL2_mixer |
47 | 13 | libSDL2_net | ||
48 | 14 | libSDL2_image | 13 | libSDL2_image |
49 | 15 | libSDL2_ttf | 14 | libSDL2_ttf |
50 | 16 | * All files from SDL2-Project | 15 | * All files from SDL2-Project |
51 | 17 | 16 | ||
52 | === modified file 'appveyor.yml' | |||
53 | --- appveyor.yml 2016-10-06 20:34:46 +0000 | |||
54 | +++ appveyor.yml 2017-06-07 18:50:40 +0000 | |||
55 | @@ -19,7 +19,7 @@ | |||
56 | 19 | - cmd: "bash --login -c \"pacman -Su --noconfirm\"" | 19 | - cmd: "bash --login -c \"pacman -Su --noconfirm\"" |
57 | 20 | - cmd: "bash --login -c \"pacman -Su --noconfirm\"" | 20 | - cmd: "bash --login -c \"pacman -Su --noconfirm\"" |
58 | 21 | # Installed required libs | 21 | # Installed required libs |
60 | 22 | - cmd: "bash --login -c \"pacman --noconfirm -S mingw-w64-%MINGWSUFFIX%-ninja mingw-w64-%MINGWSUFFIX%-boost mingw-w64-%MINGWSUFFIX%-SDL2_net mingw-w64-%MINGWSUFFIX%-SDL2_ttf mingw-w64-%MINGWSUFFIX%-SDL2_mixer mingw-w64-%MINGWSUFFIX%-SDL2_image mingw-w64-%MINGWSUFFIX%-glbinding\"" | 22 | - cmd: "bash --login -c \"pacman --noconfirm -S mingw-w64-%MINGWSUFFIX%-ninja mingw-w64-%MINGWSUFFIX%-boost mingw-w64-%MINGWSUFFIX%-SDL2_ttf mingw-w64-%MINGWSUFFIX%-SDL2_mixer mingw-w64-%MINGWSUFFIX%-SDL2_image mingw-w64-%MINGWSUFFIX%-glbinding\"" |
61 | 23 | 23 | ||
62 | 24 | shallow_clone: true | 24 | shallow_clone: true |
63 | 25 | 25 | ||
64 | 26 | 26 | ||
65 | === removed file 'cmake/Modules/FindSDL2_net.cmake' | |||
66 | --- cmake/Modules/FindSDL2_net.cmake 2014-10-13 15:04:50 +0000 | |||
67 | +++ cmake/Modules/FindSDL2_net.cmake 1970-01-01 00:00:00 +0000 | |||
68 | @@ -1,88 +0,0 @@ | |||
69 | 1 | # - Locate SDL2_net library | ||
70 | 2 | # This module defines: | ||
71 | 3 | # SDL2_NET_LIBRARIES, the name of the library to link against | ||
72 | 4 | # SDL2_NET_INCLUDE_DIRS, where to find the headers | ||
73 | 5 | # SDL2_NET_FOUND, if false, do not try to link against | ||
74 | 6 | # SDL2_NET_VERSION_STRING - human-readable string containing the version of SDL2_net | ||
75 | 7 | # | ||
76 | 8 | # For backward compatiblity the following variables are also set: | ||
77 | 9 | # SDL2NET_LIBRARY (same value as SDL2_NET_LIBRARIES) | ||
78 | 10 | # SDL2NET_INCLUDE_DIR (same value as SDL2_NET_INCLUDE_DIRS) | ||
79 | 11 | # SDL2NET_FOUND (same value as SDL2_NET_FOUND) | ||
80 | 12 | # | ||
81 | 13 | # $SDL2DIR is an environment variable that would | ||
82 | 14 | # correspond to the ./configure --prefix=$SDL2DIR | ||
83 | 15 | # used in building SDL2. | ||
84 | 16 | # | ||
85 | 17 | # Created by Eric Wing. This was influenced by the FindSDL2.cmake | ||
86 | 18 | # module, but with modifications to recognize OS X frameworks and | ||
87 | 19 | # additional Unix paths (FreeBSD, etc). | ||
88 | 20 | |||
89 | 21 | #============================================================================= | ||
90 | 22 | # Copyright 2005-2009 Kitware, Inc. | ||
91 | 23 | # Copyright 2012 Benjamin Eikel | ||
92 | 24 | # | ||
93 | 25 | # Distributed under the OSI-approved BSD License (the "License"); | ||
94 | 26 | # see accompanying file Copyright.txt for details. | ||
95 | 27 | # | ||
96 | 28 | # This software is distributed WITHOUT ANY WARRANTY; without even the | ||
97 | 29 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
98 | 30 | # See the License for more information. | ||
99 | 31 | #============================================================================= | ||
100 | 32 | # (To distribute this file outside of CMake, substitute the full | ||
101 | 33 | # License text for the above reference.) | ||
102 | 34 | |||
103 | 35 | if(NOT SDL2_NET_INCLUDE_DIR AND SDL2NET_INCLUDE_DIR) | ||
104 | 36 | set(SDL2_NET_INCLUDE_DIR ${SDL2NET_INCLUDE_DIR} CACHE PATH "directory cache | ||
105 | 37 | entry initialized from old variable name") | ||
106 | 38 | endif() | ||
107 | 39 | find_path(SDL2_NET_INCLUDE_DIR SDL_net.h | ||
108 | 40 | HINTS | ||
109 | 41 | ENV SDL2NETDIR | ||
110 | 42 | ENV SDL2DIR | ||
111 | 43 | PATH_SUFFIXES include/SDL2 include | ||
112 | 44 | ) | ||
113 | 45 | |||
114 | 46 | if(NOT SDL2_NET_LIBRARY AND SDL2NET_LIBRARY) | ||
115 | 47 | set(SDL2_NET_LIBRARY ${SDL2NET_LIBRARY} CACHE FILEPATH "file cache entry | ||
116 | 48 | initialized from old variable name") | ||
117 | 49 | endif() | ||
118 | 50 | find_library(SDL2_NET_LIBRARY | ||
119 | 51 | NAMES SDL2_net | ||
120 | 52 | HINTS | ||
121 | 53 | ENV SDL2NETDIR | ||
122 | 54 | ENV SDL2DIR | ||
123 | 55 | PATH_SUFFIXES lib | ||
124 | 56 | ) | ||
125 | 57 | |||
126 | 58 | if(SDL2_NET_INCLUDE_DIR AND EXISTS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h") | ||
127 | 59 | file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL_NET_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_NET_MAJOR_VERSION[ \t]+[0-9]+$") | ||
128 | 60 | file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL_NET_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_NET_MINOR_VERSION[ \t]+[0-9]+$") | ||
129 | 61 | file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL_NET_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_NET_PATCHLEVEL[ \t]+[0-9]+$") | ||
130 | 62 | string(REGEX REPLACE "^#define[ \t]+SDL_NET_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL_NET_VERSION_MAJOR "${SDL_NET_VERSION_MAJOR_LINE}") | ||
131 | 63 | string(REGEX REPLACE "^#define[ \t]+SDL_NET_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL_NET_VERSION_MINOR "${SDL_NET_VERSION_MINOR_LINE}") | ||
132 | 64 | string(REGEX REPLACE "^#define[ \t]+SDL_NET_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL_NET_VERSION_PATCH "${SDL_NET_VERSION_PATCH_LINE}") | ||
133 | 65 | set(SDL2_NET_VERSION_STRING ${SDL_NET_VERSION_MAJOR}.${SDL_NET_VERSION_MINOR}.${SDL_NET_VERSION_PATCH}) | ||
134 | 66 | unset(SDL_NET_VERSION_MAJOR_LINE) | ||
135 | 67 | unset(SDL_NET_VERSION_MINOR_LINE) | ||
136 | 68 | unset(SDL_NET_VERSION_PATCH_LINE) | ||
137 | 69 | unset(SDL_NET_VERSION_MAJOR) | ||
138 | 70 | unset(SDL_NET_VERSION_MINOR) | ||
139 | 71 | unset(SDL_NET_VERSION_PATCH) | ||
140 | 72 | endif() | ||
141 | 73 | |||
142 | 74 | set(SDL2_NET_LIBRARIES ${SDL2_NET_LIBRARY}) | ||
143 | 75 | set(SDL2_NET_INCLUDE_DIRS ${SDL2_NET_INCLUDE_DIR}) | ||
144 | 76 | |||
145 | 77 | # include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) | ||
146 | 78 | |||
147 | 79 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_net | ||
148 | 80 | REQUIRED_VARS SDL2_NET_LIBRARIES SDL2_NET_INCLUDE_DIRS | ||
149 | 81 | VERSION_VAR SDL2_NET_VERSION_STRING) | ||
150 | 82 | |||
151 | 83 | # for backward compatiblity | ||
152 | 84 | set(SDL2NET_LIBRARY ${SDL2_NET_LIBRARIES}) | ||
153 | 85 | set(SDL2NET_INCLUDE_DIR ${SDL2_NET_INCLUDE_DIRS}) | ||
154 | 86 | set(SDL2NET_FOUND ${SDL2_NET_FOUND}) | ||
155 | 87 | |||
156 | 88 | mark_as_advanced(SDL2_NET_LIBRARY SDL2_NET_INCLUDE_DIR) | ||
157 | 89 | 0 | ||
158 | === modified file 'cmake/WlFunctions.cmake' | |||
159 | --- cmake/WlFunctions.cmake 2016-02-06 16:17:23 +0000 | |||
160 | +++ cmake/WlFunctions.cmake 2017-06-07 18:50:40 +0000 | |||
161 | @@ -12,7 +12,6 @@ | |||
162 | 12 | USES_SDL2 | 12 | USES_SDL2 |
163 | 13 | USES_SDL2_IMAGE | 13 | USES_SDL2_IMAGE |
164 | 14 | USES_SDL2_MIXER | 14 | USES_SDL2_MIXER |
165 | 15 | USES_SDL2_NET | ||
166 | 16 | USES_SDL2_TTF | 15 | USES_SDL2_TTF |
167 | 17 | USES_ZLIB | 16 | USES_ZLIB |
168 | 18 | USES_ICU | 17 | USES_ICU |
169 | @@ -127,11 +126,6 @@ | |||
170 | 127 | target_link_libraries(${NAME} ${SDL2MIXER_LIBRARY}) | 126 | target_link_libraries(${NAME} ${SDL2MIXER_LIBRARY}) |
171 | 128 | endif() | 127 | endif() |
172 | 129 | 128 | ||
173 | 130 | if(ARG_USES_SDL2_NET) | ||
174 | 131 | wl_include_system_directories(${NAME} ${SDL2NET_INCLUDE_DIR}) | ||
175 | 132 | target_link_libraries(${NAME} ${SDL2NET_LIBRARY}) | ||
176 | 133 | endif() | ||
177 | 134 | |||
178 | 135 | if(ARG_USES_SDL2_IMAGE) | 129 | if(ARG_USES_SDL2_IMAGE) |
179 | 136 | wl_include_system_directories(${NAME} ${SDL2IMAGE_INCLUDE_DIR}) | 130 | wl_include_system_directories(${NAME} ${SDL2IMAGE_INCLUDE_DIR}) |
180 | 137 | target_link_libraries(${NAME} ${SDL2IMAGE_LIBRARY}) | 131 | target_link_libraries(${NAME} ${SDL2IMAGE_LIBRARY}) |
181 | 138 | 132 | ||
182 | === modified file 'src/network/CMakeLists.txt' | |||
183 | --- src/network/CMakeLists.txt 2017-05-07 20:27:21 +0000 | |||
184 | +++ src/network/CMakeLists.txt 2017-06-07 18:50:40 +0000 | |||
185 | @@ -23,8 +23,6 @@ | |||
186 | 23 | network_player_settings_backend.cc | 23 | network_player_settings_backend.cc |
187 | 24 | network_player_settings_backend.h | 24 | network_player_settings_backend.h |
188 | 25 | network_protocol.h | 25 | network_protocol.h |
189 | 26 | network_system.h | ||
190 | 27 | USES_SDL2_NET | ||
191 | 28 | DEPENDS | 26 | DEPENDS |
192 | 29 | ai | 27 | ai |
193 | 30 | base_exceptions | 28 | base_exceptions |
194 | 31 | 29 | ||
195 | === modified file 'src/network/gameclient.cc' | |||
196 | --- src/network/gameclient.cc 2017-05-11 10:45:44 +0000 | |||
197 | +++ src/network/gameclient.cc 2017-06-07 18:50:40 +0000 | |||
198 | @@ -42,7 +42,6 @@ | |||
199 | 42 | #include "network/internet_gaming.h" | 42 | #include "network/internet_gaming.h" |
200 | 43 | #include "network/network_gaming_messages.h" | 43 | #include "network/network_gaming_messages.h" |
201 | 44 | #include "network/network_protocol.h" | 44 | #include "network/network_protocol.h" |
202 | 45 | #include "network/network_system.h" | ||
203 | 46 | #include "scripting/lua_interface.h" | 45 | #include "scripting/lua_interface.h" |
204 | 47 | #include "scripting/lua_table.h" | 46 | #include "scripting/lua_table.h" |
205 | 48 | #include "ui_basic/messagebox.h" | 47 | #include "ui_basic/messagebox.h" |
206 | @@ -89,17 +88,13 @@ | |||
207 | 89 | std::vector<ChatMessage> chatmessages; | 88 | std::vector<ChatMessage> chatmessages; |
208 | 90 | }; | 89 | }; |
209 | 91 | 90 | ||
214 | 92 | GameClient::GameClient(const std::string& host, | 91 | GameClient::GameClient(const NetAddress& host, const std::string& playername, bool internet) |
211 | 93 | const uint16_t port, | ||
212 | 94 | const std::string& playername, | ||
213 | 95 | bool internet) | ||
215 | 96 | : d(new GameClientImpl), internet_(internet) { | 92 | : d(new GameClientImpl), internet_(internet) { |
217 | 97 | d->net = NetClient::connect(host, port); | 93 | d->net = NetClient::connect(host); |
218 | 98 | if (!d->net || !d->net->is_connected()) { | 94 | if (!d->net || !d->net->is_connected()) { |
219 | 99 | throw WLWarning(_("Could not establish connection to host"), | 95 | throw WLWarning(_("Could not establish connection to host"), |
223 | 100 | _("Widelands could not establish a connection to the given " | 96 | _("Widelands could not establish a connection to the given address. " |
224 | 101 | "address.\n" | 97 | "Either no Widelands server was running at the supposed port or " |
222 | 102 | "Either no Widelands server was running at the supposed port or\n" | ||
225 | 103 | "the server shut down as you tried to connect.")); | 98 | "the server shut down as you tried to connect.")); |
226 | 104 | } | 99 | } |
227 | 105 | 100 | ||
228 | 106 | 101 | ||
229 | === modified file 'src/network/gameclient.h' | |||
230 | --- src/network/gameclient.h 2017-05-11 10:45:44 +0000 | |||
231 | +++ src/network/gameclient.h 2017-06-07 18:50:40 +0000 | |||
232 | @@ -36,13 +36,11 @@ | |||
233 | 36 | * launch, as well as dealing with the actual network protocol. | 36 | * launch, as well as dealing with the actual network protocol. |
234 | 37 | */ | 37 | */ |
235 | 38 | struct GameClient : public GameController, | 38 | struct GameClient : public GameController, |
243 | 39 | public GameSettingsProvider, | 39 | public GameSettingsProvider, |
244 | 40 | private SyncCallback, | 40 | private SyncCallback, |
245 | 41 | public ChatProvider { | 41 | public ChatProvider { |
246 | 42 | GameClient(const std::string& host, | 42 | GameClient(const NetAddress& host, const std::string& playername, bool internet = false); |
247 | 43 | const uint16_t port, | 43 | |
241 | 44 | const std::string& playername, | ||
242 | 45 | bool internet = false); | ||
248 | 46 | virtual ~GameClient(); | 44 | virtual ~GameClient(); |
249 | 47 | 45 | ||
250 | 48 | void run(); | 46 | void run(); |
251 | 49 | 47 | ||
252 | === modified file 'src/network/gamehost.cc' | |||
253 | --- src/network/gamehost.cc 2017-05-11 10:45:44 +0000 | |||
254 | +++ src/network/gamehost.cc 2017-06-07 18:50:40 +0000 | |||
255 | @@ -54,7 +54,6 @@ | |||
256 | 54 | #include "network/network_lan_promotion.h" | 54 | #include "network/network_lan_promotion.h" |
257 | 55 | #include "network/network_player_settings_backend.h" | 55 | #include "network/network_player_settings_backend.h" |
258 | 56 | #include "network/network_protocol.h" | 56 | #include "network/network_protocol.h" |
259 | 57 | #include "network/network_system.h" | ||
260 | 58 | #include "profile/profile.h" | 57 | #include "profile/profile.h" |
261 | 59 | #include "scripting/lua_interface.h" | 58 | #include "scripting/lua_interface.h" |
262 | 60 | #include "ui_basic/progresswindow.h" | 59 | #include "ui_basic/progresswindow.h" |
263 | 61 | 60 | ||
264 | === modified file 'src/network/internet_gaming.cc' | |||
265 | --- src/network/internet_gaming.cc 2017-05-16 18:29:06 +0000 | |||
266 | +++ src/network/internet_gaming.cc 2017-06-07 18:50:40 +0000 | |||
267 | @@ -95,7 +95,10 @@ | |||
268 | 95 | void InternetGaming::initialize_connection() { | 95 | void InternetGaming::initialize_connection() { |
269 | 96 | // First of all try to connect to the metaserver | 96 | // First of all try to connect to the metaserver |
270 | 97 | log("InternetGaming: Connecting to the metaserver.\n"); | 97 | log("InternetGaming: Connecting to the metaserver.\n"); |
272 | 98 | net = NetClient::connect(meta_, port_); | 98 | NetAddress addr; |
273 | 99 | net.reset(); | ||
274 | 100 | if (NetAddress::resolve_to_v4(&addr, meta_, port_)) | ||
275 | 101 | net = NetClient::connect(addr); | ||
276 | 99 | if (!net || !net->is_connected()) | 102 | if (!net || !net->is_connected()) |
277 | 100 | throw WLWarning(_("Could not establish connection to host"), | 103 | throw WLWarning(_("Could not establish connection to host"), |
278 | 101 | _("Widelands could not establish a connection to the given address.\n" | 104 | _("Widelands could not establish a connection to the given address.\n" |
279 | @@ -216,10 +219,12 @@ | |||
280 | 216 | void InternetGaming::logout(const std::string& msgcode) { | 219 | void InternetGaming::logout(const std::string& msgcode) { |
281 | 217 | 220 | ||
282 | 218 | // Just in case the metaserver is listening on the socket - tell him we break up with him ;) | 221 | // Just in case the metaserver is listening on the socket - tell him we break up with him ;) |
287 | 219 | SendPacket s; | 222 | if (net && net->is_connected()) { |
288 | 220 | s.string(IGPCMD_DISCONNECT); | 223 | SendPacket s; |
289 | 221 | s.string(msgcode); | 224 | s.string(IGPCMD_DISCONNECT); |
290 | 222 | net->send(s); | 225 | s.string(msgcode); |
291 | 226 | net->send(s); | ||
292 | 227 | } | ||
293 | 223 | 228 | ||
294 | 224 | const std::string& msg = InternetGamingMessages::get_message(msgcode); | 229 | const std::string& msg = InternetGamingMessages::get_message(msgcode); |
295 | 225 | log("InternetGaming: logout(%s)\n", msg.c_str()); | 230 | log("InternetGaming: logout(%s)\n", msg.c_str()); |
296 | 226 | 231 | ||
297 | === modified file 'src/network/internet_gaming.h' | |||
298 | --- src/network/internet_gaming.h 2017-05-09 19:17:12 +0000 | |||
299 | +++ src/network/internet_gaming.h 2017-06-07 18:50:40 +0000 | |||
300 | @@ -24,11 +24,6 @@ | |||
301 | 24 | #include <string> | 24 | #include <string> |
302 | 25 | #include <vector> | 25 | #include <vector> |
303 | 26 | 26 | ||
304 | 27 | #ifdef _WIN32 | ||
305 | 28 | #include <io.h> | ||
306 | 29 | #include <winsock2.h> | ||
307 | 30 | #endif | ||
308 | 31 | |||
309 | 32 | #include "build_info.h" | 27 | #include "build_info.h" |
310 | 33 | #include "chat/chat.h" | 28 | #include "chat/chat.h" |
311 | 34 | #include "network/internet_gaming_protocol.h" | 29 | #include "network/internet_gaming_protocol.h" |
312 | @@ -170,7 +165,7 @@ | |||
313 | 170 | std::string pwd_; | 165 | std::string pwd_; |
314 | 171 | bool reg_; | 166 | bool reg_; |
315 | 172 | std::string meta_; | 167 | std::string meta_; |
317 | 173 | uint32_t port_; | 168 | uint16_t port_; |
318 | 174 | 169 | ||
319 | 175 | /// local clients name and rights | 170 | /// local clients name and rights |
320 | 176 | std::string clientname_; | 171 | std::string clientname_; |
321 | 177 | 172 | ||
322 | === modified file 'src/network/netclient.cc' | |||
323 | --- src/network/netclient.cc 2017-05-11 10:45:44 +0000 | |||
324 | +++ src/network/netclient.cc 2017-06-07 18:50:40 +0000 | |||
325 | @@ -4,8 +4,9 @@ | |||
326 | 4 | 4 | ||
327 | 5 | #include "base/log.h" | 5 | #include "base/log.h" |
328 | 6 | 6 | ||
331 | 7 | std::unique_ptr<NetClient> NetClient::connect(const std::string& ip_address, const uint16_t port) { | 7 | std::unique_ptr<NetClient> NetClient::connect(const NetAddress& host) { |
332 | 8 | std::unique_ptr<NetClient> ptr(new NetClient(ip_address, port)); | 8 | |
333 | 9 | std::unique_ptr<NetClient> ptr(new NetClient(host)); | ||
334 | 9 | if (ptr->is_connected()) { | 10 | if (ptr->is_connected()) { |
335 | 10 | return ptr; | 11 | return ptr; |
336 | 11 | } else { | 12 | } else { |
337 | @@ -17,63 +18,82 @@ | |||
338 | 17 | NetClient::~NetClient() { | 18 | NetClient::~NetClient() { |
339 | 18 | if (is_connected()) | 19 | if (is_connected()) |
340 | 19 | close(); | 20 | close(); |
341 | 20 | if (sockset_ != nullptr) | ||
342 | 21 | SDLNet_FreeSocketSet(sockset_); | ||
343 | 22 | } | 21 | } |
344 | 23 | 22 | ||
345 | 24 | bool NetClient::is_connected() const { | 23 | bool NetClient::is_connected() const { |
347 | 25 | return sock_ != nullptr; | 24 | return socket_.is_open(); |
348 | 26 | } | 25 | } |
349 | 27 | 26 | ||
350 | 28 | void NetClient::close() { | 27 | void NetClient::close() { |
351 | 29 | if (!is_connected()) | 28 | if (!is_connected()) |
352 | 30 | return; | 29 | return; |
356 | 31 | SDLNet_TCP_DelSocket(sockset_, sock_); | 30 | boost::system::error_code ec; |
357 | 32 | SDLNet_TCP_Close(sock_); | 31 | boost::asio::ip::tcp::endpoint remote = socket_.remote_endpoint(ec); |
358 | 33 | sock_ = nullptr; | 32 | if (!ec) { |
359 | 33 | log("[NetClient] Closing network socket connected to %s:%i.\n", | ||
360 | 34 | remote.address().to_string().c_str(), remote.port()); | ||
361 | 35 | } else { | ||
362 | 36 | log("[NetClient] Closing network socket.\n"); | ||
363 | 37 | } | ||
364 | 38 | socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); | ||
365 | 39 | socket_.close(ec); | ||
366 | 34 | } | 40 | } |
367 | 35 | 41 | ||
368 | 36 | bool NetClient::try_receive(RecvPacket* packet) { | 42 | bool NetClient::try_receive(RecvPacket* packet) { |
369 | 37 | if (!is_connected()) | 43 | if (!is_connected()) |
370 | 38 | return false; | 44 | return false; |
371 | 39 | 45 | ||
383 | 40 | uint8_t buffer[512]; | 46 | uint8_t buffer[kNetworkBufferSize]; |
384 | 41 | while (SDLNet_CheckSockets(sockset_, 0) > 0) { | 47 | boost::system::error_code ec; |
385 | 42 | 48 | size_t length = socket_.read_some(boost::asio::buffer(buffer, kNetworkBufferSize), ec); | |
386 | 43 | const int32_t bytes = SDLNet_TCP_Recv(sock_, buffer, sizeof(buffer)); | 49 | if (!ec) { |
387 | 44 | if (bytes <= 0) { | 50 | assert(length > 0); |
388 | 45 | // Error while receiving | 51 | assert(length <= kNetworkBufferSize); |
389 | 46 | close(); | 52 | // Has read something |
390 | 47 | return false; | 53 | deserializer_.read_data(buffer, length); |
391 | 48 | } | 54 | } |
392 | 49 | 55 | ||
393 | 50 | deserializer_.read_data(buffer, bytes); | 56 | if (ec && ec != boost::asio::error::would_block) { |
394 | 57 | // Connection closed or some error, close the socket | ||
395 | 58 | log("[NetClient] Error when trying to receive some data: %s.\n", ec.message().c_str()); | ||
396 | 59 | close(); | ||
397 | 60 | return false; | ||
398 | 51 | } | 61 | } |
399 | 52 | // Get one packet from the deserializer | 62 | // Get one packet from the deserializer |
400 | 53 | return deserializer_.write_packet(packet); | 63 | return deserializer_.write_packet(packet); |
401 | 54 | } | 64 | } |
402 | 55 | 65 | ||
403 | 56 | void NetClient::send(const SendPacket& packet) { | 66 | void NetClient::send(const SendPacket& packet) { |
415 | 57 | if (is_connected()) { | 67 | if (!is_connected()) |
405 | 58 | SDLNet_TCP_Send(sock_, packet.get_data(), packet.get_size()); | ||
406 | 59 | } | ||
407 | 60 | } | ||
408 | 61 | |||
409 | 62 | NetClient::NetClient(const std::string& ip_address, const uint16_t port) | ||
410 | 63 | : sock_(nullptr), sockset_(nullptr), deserializer_() { | ||
411 | 64 | |||
412 | 65 | IPaddress addr; | ||
413 | 66 | if (SDLNet_ResolveHost(&addr, ip_address.c_str(), port) != 0) { | ||
414 | 67 | log("[Client]: Failed to resolve host address %s:%u.\n", ip_address.c_str(), port); | ||
416 | 68 | return; | 68 | return; |
417 | 69 | |||
418 | 70 | boost::system::error_code ec; | ||
419 | 71 | size_t written = boost::asio::write(socket_, | ||
420 | 72 | boost::asio::buffer(packet.get_data(), packet.get_size()), ec); | ||
421 | 73 | // TODO(Notabilis): This one is an assertion of mine, I am not sure if it will hold | ||
422 | 74 | // If it doesn't, set the socket to blocking before writing | ||
423 | 75 | // If it does, remove this comment after build 20 | ||
424 | 76 | assert(ec != boost::asio::error::would_block); | ||
425 | 77 | assert(written == packet.get_size() || ec); | ||
426 | 78 | if (ec) { | ||
427 | 79 | log("[NetClient] Error when trying to send some data: %s.\n", ec.message().c_str()); | ||
428 | 80 | close(); | ||
429 | 69 | } | 81 | } |
436 | 70 | log("[Client]: Trying to connect to %s:%u ... ", ip_address.c_str(), port); | 82 | } |
437 | 71 | sock_ = SDLNet_TCP_Open(&addr); | 83 | |
438 | 72 | if (is_connected()) { | 84 | NetClient::NetClient(const NetAddress& host) |
439 | 73 | log("success\n"); | 85 | : io_service_(), socket_(io_service_), deserializer_() { |
440 | 74 | sockset_ = SDLNet_AllocSocketSet(1); | 86 | |
441 | 75 | SDLNet_TCP_AddSocket(sockset_, sock_); | 87 | assert(host.is_valid()); |
442 | 88 | const boost::asio::ip::tcp::endpoint destination(host.ip, host.port); | ||
443 | 89 | |||
444 | 90 | log("[NetClient]: Trying to connect to %s:%u ... ", host.ip.to_string().c_str(), host.port); | ||
445 | 91 | boost::system::error_code ec; | ||
446 | 92 | socket_.connect(destination, ec); | ||
447 | 93 | if (!ec && is_connected()) { | ||
448 | 94 | log("success.\n"); | ||
449 | 95 | socket_.non_blocking(true); | ||
450 | 76 | } else { | 96 | } else { |
452 | 77 | log("failed\n"); | 97 | log("failed.\n"); |
453 | 78 | } | 98 | } |
454 | 79 | } | 99 | } |
455 | 80 | 100 | ||
456 | === modified file 'src/network/netclient.h' | |||
457 | --- src/network/netclient.h 2017-05-11 10:45:44 +0000 | |||
458 | +++ src/network/netclient.h 2017-06-07 18:50:40 +0000 | |||
459 | @@ -22,23 +22,23 @@ | |||
460 | 22 | 22 | ||
461 | 23 | #include <memory> | 23 | #include <memory> |
462 | 24 | 24 | ||
463 | 25 | #include <SDL_net.h> | ||
464 | 26 | |||
465 | 27 | #include "network/network.h" | 25 | #include "network/network.h" |
466 | 28 | 26 | ||
467 | 29 | /** | 27 | /** |
468 | 30 | * 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 |
469 | 31 | * participates as a client. | 29 | * participates as a client. |
470 | 30 | * This class only tries to create a single socket, either for IPv4 and IPv6. | ||
471 | 31 | * Which is used depends on what kind of address is given on call to connect(). | ||
472 | 32 | */ | 32 | */ |
473 | 33 | class NetClient { | 33 | class NetClient { |
474 | 34 | public: | 34 | public: |
475 | 35 | |||
476 | 35 | /** | 36 | /** |
477 | 36 | * Tries to establish a connection to the given host. | 37 | * Tries to establish a connection to the given host. |
481 | 37 | * @param ip_address A hostname or an IPv4 address as string. | 38 | * \param host The host to connect to. |
482 | 38 | * @param port The port to connect to. | 39 | * \return A pointer to a connected \c NetClient object or a \c nullptr if the connection failed. |
480 | 39 | * @return A pointer to a connected \c NetClient object or a nullptr if the connection failed. | ||
483 | 40 | */ | 40 | */ |
485 | 41 | static std::unique_ptr<NetClient> connect(const std::string& ip_address, const uint16_t port); | 41 | static std::unique_ptr<NetClient> connect(const NetAddress& host); |
486 | 42 | 42 | ||
487 | 43 | /** | 43 | /** |
488 | 44 | * Closes the connection. | 44 | * Closes the connection. |
489 | @@ -48,7 +48,7 @@ | |||
490 | 48 | 48 | ||
491 | 49 | /** | 49 | /** |
492 | 50 | * Returns whether the client is connected. | 50 | * Returns whether the client is connected. |
494 | 51 | * @return \c true if the connection is open, \c false otherwise. | 51 | * \return \c true if the connection is open, \c false otherwise. |
495 | 52 | */ | 52 | */ |
496 | 53 | bool is_connected() const; | 53 | bool is_connected() const; |
497 | 54 | 54 | ||
498 | @@ -60,30 +60,35 @@ | |||
499 | 60 | 60 | ||
500 | 61 | /** | 61 | /** |
501 | 62 | * Tries to receive a packet. | 62 | * Tries to receive a packet. |
504 | 63 | * @param packet A packet that should be overwritten with the received data. | 63 | * \param packet A packet that should be overwritten with the received data. |
505 | 64 | * @return \c true if a packet is available, \c false otherwise. | 64 | * \return \c true if a packet is available, \c false otherwise. |
506 | 65 | * The given packet is only modified when \c true is returned. | 65 | * The given packet is only modified when \c true is returned. |
507 | 66 | * Calling this on a closed connection will return false. | 66 | * Calling this on a closed connection will return false. |
508 | 67 | */ | 67 | */ |
510 | 68 | bool try_receive(RecvPacket* packet); | 68 | bool try_receive(RecvPacket *packet); |
511 | 69 | 69 | ||
512 | 70 | /** | 70 | /** |
513 | 71 | * Sends a packet. | 71 | * Sends a packet. |
514 | 72 | * Calling this on a closed connection will silently fail. | 72 | * Calling this on a closed connection will silently fail. |
516 | 73 | * @param packet The packet to send. | 73 | * \param packet The packet to send. |
517 | 74 | */ | 74 | */ |
519 | 75 | void send(const SendPacket& packet); | 75 | void send(const SendPacket& packet); |
520 | 76 | 76 | ||
521 | 77 | private: | 77 | private: |
531 | 78 | NetClient(const std::string& ip_address, const uint16_t port); | 78 | /** |
532 | 79 | 79 | * Tries to establish a connection to the given host. | |
533 | 80 | /// The socket that connects us to the host | 80 | * If the connection attempt failed, is_connected() will return \c false. |
534 | 81 | TCPsocket sock_; | 81 | * \param host The host to connect to. |
535 | 82 | 82 | */ | |
536 | 83 | /// Socket set used for selection | 83 | NetClient(const NetAddress& host); |
537 | 84 | SDLNet_SocketSet sockset_; | 84 | |
538 | 85 | 85 | /// An io_service needed by boost.asio. Primarily needed for asynchronous operations. | |
539 | 86 | /// Deserializer acts as a buffer for packets (reassembly/splitting up) | 86 | boost::asio::io_service io_service_; |
540 | 87 | |||
541 | 88 | /// The socket that connects us to the host. | ||
542 | 89 | boost::asio::ip::tcp::socket socket_; | ||
543 | 90 | |||
544 | 91 | /// Deserializer acts as a buffer for packets (splitting stream to packets) | ||
545 | 87 | Deserializer deserializer_; | 92 | Deserializer deserializer_; |
546 | 88 | }; | 93 | }; |
547 | 89 | 94 | ||
548 | 90 | 95 | ||
549 | === modified file 'src/network/nethost.cc' | |||
550 | --- src/network/nethost.cc 2017-05-11 10:45:44 +0000 | |||
551 | +++ src/network/nethost.cc 2017-06-07 18:50:40 +0000 | |||
552 | @@ -4,7 +4,25 @@ | |||
553 | 4 | 4 | ||
554 | 5 | #include "base/log.h" | 5 | #include "base/log.h" |
555 | 6 | 6 | ||
557 | 7 | NetHost::Client::Client(TCPsocket sock) : socket(sock), deserializer() { | 7 | namespace { |
558 | 8 | |||
559 | 9 | /** | ||
560 | 10 | * Returns the IP version. | ||
561 | 11 | * \param acceptor The acceptor socket to get the IP version for. | ||
562 | 12 | * \return Either 4 or 6, depending on the version of the given acceptor. | ||
563 | 13 | */ | ||
564 | 14 | int get_ip_version(const boost::asio::ip::tcp::acceptor& acceptor) { | ||
565 | 15 | assert(acceptor.is_open()); | ||
566 | 16 | if (acceptor.local_endpoint().protocol() == boost::asio::ip::tcp::v4()) { | ||
567 | 17 | return 4; | ||
568 | 18 | } else { | ||
569 | 19 | return 6; | ||
570 | 20 | } | ||
571 | 21 | } | ||
572 | 22 | } | ||
573 | 23 | |||
574 | 24 | |||
575 | 25 | NetHost::Client::Client(boost::asio::ip::tcp::socket&& sock) : socket(std::move(sock)), deserializer() { | ||
576 | 8 | } | 26 | } |
577 | 9 | 27 | ||
578 | 10 | std::unique_ptr<NetHost> NetHost::listen(const uint16_t port) { | 28 | std::unique_ptr<NetHost> NetHost::listen(const uint16_t port) { |
579 | @@ -22,11 +40,10 @@ | |||
580 | 22 | while (!clients_.empty()) { | 40 | while (!clients_.empty()) { |
581 | 23 | close(clients_.begin()->first); | 41 | close(clients_.begin()->first); |
582 | 24 | } | 42 | } |
583 | 25 | SDLNet_FreeSocketSet(sockset_); | ||
584 | 26 | } | 43 | } |
585 | 27 | 44 | ||
586 | 28 | bool NetHost::is_listening() const { | 45 | bool NetHost::is_listening() const { |
588 | 29 | return svrsock_ != nullptr; | 46 | return acceptor_v4_.is_open() || acceptor_v6_.is_open(); |
589 | 30 | } | 47 | } |
590 | 31 | 48 | ||
591 | 32 | bool NetHost::is_connected(const ConnectionId id) const { | 49 | bool NetHost::is_connected(const ConnectionId id) const { |
592 | @@ -34,11 +51,17 @@ | |||
593 | 34 | } | 51 | } |
594 | 35 | 52 | ||
595 | 36 | void NetHost::stop_listening() { | 53 | void NetHost::stop_listening() { |
601 | 37 | if (!is_listening()) | 54 | static const auto do_stop = [](boost::asio::ip::tcp::acceptor& acceptor) { |
602 | 38 | return; | 55 | boost::system::error_code ec; |
603 | 39 | SDLNet_TCP_DelSocket(sockset_, svrsock_); | 56 | if (acceptor.is_open()) { |
604 | 40 | SDLNet_TCP_Close(svrsock_); | 57 | log("[NetHost]: Closing a listening IPv%d socket.\n", get_ip_version(acceptor)); |
605 | 41 | svrsock_ = nullptr; | 58 | acceptor.close(ec); |
606 | 59 | } | ||
607 | 60 | // Ignore errors | ||
608 | 61 | }; | ||
609 | 62 | |||
610 | 63 | do_stop(acceptor_v4_); | ||
611 | 64 | do_stop(acceptor_v6_); | ||
612 | 42 | } | 65 | } |
613 | 43 | 66 | ||
614 | 44 | void NetHost::close(const ConnectionId id) { | 67 | void NetHost::close(const ConnectionId id) { |
615 | @@ -47,68 +70,145 @@ | |||
616 | 47 | // Not connected anyway | 70 | // Not connected anyway |
617 | 48 | return; | 71 | return; |
618 | 49 | } | 72 | } |
621 | 50 | SDLNet_TCP_DelSocket(sockset_, iter_client->second.socket); | 73 | boost::system::error_code ec; |
622 | 51 | SDLNet_TCP_Close(iter_client->second.socket); | 74 | boost::asio::ip::tcp::endpoint remote = iter_client->second.socket.remote_endpoint(ec); |
623 | 75 | if (!ec) { | ||
624 | 76 | log("[NetHost] Closing network connection to %s:%i.\n", | ||
625 | 77 | remote.address().to_string().c_str(), remote.port()); | ||
626 | 78 | } else { | ||
627 | 79 | log("[NetHost] Closing network connection to some client.\n"); | ||
628 | 80 | } | ||
629 | 81 | iter_client->second.socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); | ||
630 | 82 | iter_client->second.socket.close(ec); | ||
631 | 52 | clients_.erase(iter_client); | 83 | clients_.erase(iter_client); |
632 | 53 | } | 84 | } |
633 | 54 | 85 | ||
634 | 55 | bool NetHost::try_accept(ConnectionId* new_id) { | 86 | bool NetHost::try_accept(ConnectionId* new_id) { |
635 | 56 | if (!is_listening()) | 87 | if (!is_listening()) |
636 | 57 | return false; | 88 | return false; |
641 | 58 | 89 | boost::asio::ip::tcp::socket socket(io_service_); | |
642 | 59 | TCPsocket sock = SDLNet_TCP_Accept(svrsock_); | 90 | |
643 | 60 | // No client wants to connect | 91 | const auto do_try_accept = [&socket](boost::asio::ip::tcp::acceptor& acceptor) { |
644 | 61 | if (sock == nullptr) | 92 | boost::system::error_code ec; |
645 | 93 | if (acceptor.is_open()) { | ||
646 | 94 | acceptor.accept(socket, ec); | ||
647 | 95 | if (ec == boost::asio::error::would_block) { | ||
648 | 96 | // No client wants to connect | ||
649 | 97 | // New socket doesn't need to be closed since it isn't open yet | ||
650 | 98 | } else if (ec) { | ||
651 | 99 | // Some other error, close the acceptor | ||
652 | 100 | log("[NetHost] No longer listening for IPv%d connections due to error: %s.\n", | ||
653 | 101 | get_ip_version(acceptor), ec.message().c_str()); | ||
654 | 102 | acceptor.close(ec); | ||
655 | 103 | } else { | ||
656 | 104 | log("[NetHost]: Accepting IPv%d connection from %s.\n", | ||
657 | 105 | get_ip_version(acceptor), socket.remote_endpoint().address().to_string().c_str()); | ||
658 | 106 | } | ||
659 | 107 | } | ||
660 | 108 | }; | ||
661 | 109 | |||
662 | 110 | do_try_accept(acceptor_v4_); | ||
663 | 111 | if (!socket.is_open()) | ||
664 | 112 | do_try_accept(acceptor_v6_); | ||
665 | 113 | |||
666 | 114 | if (!socket.is_open()) { | ||
667 | 115 | // No new connection | ||
668 | 62 | return false; | 116 | return false; |
670 | 63 | SDLNet_TCP_AddSocket(sockset_, sock); | 117 | } |
671 | 118 | |||
672 | 119 | socket.non_blocking(true); | ||
673 | 120 | |||
674 | 64 | ConnectionId id = next_id_++; | 121 | ConnectionId id = next_id_++; |
675 | 65 | assert(id > 0); | 122 | assert(id > 0); |
676 | 66 | assert(clients_.count(id) == 0); | 123 | assert(clients_.count(id) == 0); |
678 | 67 | clients_.insert(std::make_pair(id, Client(sock))); | 124 | clients_.insert(std::make_pair(id, Client(std::move(socket)))); |
679 | 68 | assert(clients_.count(id) == 1); | 125 | assert(clients_.count(id) == 1); |
680 | 69 | *new_id = id; | 126 | *new_id = id; |
681 | 70 | return true; | 127 | return true; |
682 | 71 | } | 128 | } |
683 | 72 | 129 | ||
684 | 73 | bool NetHost::try_receive(const ConnectionId id, RecvPacket* packet) { | 130 | bool NetHost::try_receive(const ConnectionId id, RecvPacket* packet) { |
685 | 74 | |||
686 | 75 | // Always read all available data into buffers | 131 | // Always read all available data into buffers |
698 | 76 | uint8_t buffer[512]; | 132 | uint8_t buffer[kNetworkBufferSize]; |
688 | 77 | while (SDLNet_CheckSockets(sockset_, 0) > 0) { | ||
689 | 78 | for (auto& e : clients_) { | ||
690 | 79 | if (SDLNet_SocketReady(e.second.socket)) { | ||
691 | 80 | const int32_t bytes = SDLNet_TCP_Recv(e.second.socket, buffer, sizeof(buffer)); | ||
692 | 81 | if (bytes <= 0) { | ||
693 | 82 | // Error while receiving | ||
694 | 83 | close(e.first); | ||
695 | 84 | // We have to run the for-loop again since we modified the map | ||
696 | 85 | break; | ||
697 | 86 | } | ||
699 | 87 | 133 | ||
702 | 88 | e.second.deserializer.read_data(buffer, bytes); | 134 | boost::system::error_code ec; |
703 | 89 | } | 135 | for (auto it = clients_.begin(); it != clients_.end(); ) { |
704 | 136 | size_t length = it->second.socket.read_some(boost::asio::buffer(buffer, kNetworkBufferSize), ec); | ||
705 | 137 | if (ec == boost::asio::error::would_block) { | ||
706 | 138 | // Nothing to read | ||
707 | 139 | assert(length == 0); | ||
708 | 140 | ++it; | ||
709 | 141 | continue; | ||
710 | 142 | } else if (ec) { | ||
711 | 143 | assert(length == 0); | ||
712 | 144 | // Connection closed or some error, close the socket | ||
713 | 145 | log("[NetHost] Error when receiving from a client, closing connection: %s.\n", | ||
714 | 146 | ec.message().c_str()); | ||
715 | 147 | // close() will remove the client from the map so we have to increment the iterator first. | ||
716 | 148 | // Otherwise, it will point to unallocated memory after close() so we can't increase it | ||
717 | 149 | ConnectionId id_to_remove = it->first; | ||
718 | 150 | ++it; | ||
719 | 151 | close(id_to_remove); | ||
720 | 152 | continue; | ||
721 | 90 | } | 153 | } |
722 | 154 | assert(length > 0); | ||
723 | 155 | assert(length <= kNetworkBufferSize); | ||
724 | 156 | // Read something | ||
725 | 157 | it->second.deserializer.read_data(buffer, length); | ||
726 | 158 | ++it; | ||
727 | 91 | } | 159 | } |
728 | 92 | 160 | ||
729 | 93 | // Now check whether there is data for the requested client | 161 | // Now check whether there is data for the requested client |
730 | 94 | if (!is_connected(id)) | 162 | if (!is_connected(id)) |
731 | 95 | return false; | 163 | return false; |
732 | 96 | 164 | ||
734 | 97 | // Get one packet from the deserializer | 165 | // Try to get one packet from the deserializer |
735 | 98 | return clients_.at(id).deserializer.write_packet(packet); | 166 | return clients_.at(id).deserializer.write_packet(packet); |
736 | 99 | } | 167 | } |
737 | 100 | 168 | ||
738 | 101 | void NetHost::send(const ConnectionId id, const SendPacket& packet) { | 169 | void NetHost::send(const ConnectionId id, const SendPacket& packet) { |
739 | 170 | boost::system::error_code ec; | ||
740 | 102 | if (is_connected(id)) { | 171 | if (is_connected(id)) { |
752 | 103 | SDLNet_TCP_Send(clients_.at(id).socket, packet.get_data(), packet.get_size()); | 172 | size_t written = boost::asio::write(clients_.at(id).socket, |
753 | 104 | } | 173 | boost::asio::buffer(packet.get_data(), packet.get_size()), ec); |
754 | 105 | } | 174 | // TODO(Notabilis): This one is an assertion of mine, I am not sure if it will hold |
755 | 106 | 175 | // If it doesn't, set the socket to blocking before writing | |
756 | 107 | NetHost::NetHost(const uint16_t port) : svrsock_(nullptr), sockset_(nullptr), next_id_(1) { | 176 | // If it does, remove this comment after build 20 |
757 | 108 | 177 | assert(ec != boost::asio::error::would_block); | |
758 | 109 | IPaddress myaddr; | 178 | assert(written == packet.get_size() || ec); |
759 | 110 | SDLNet_ResolveHost(&myaddr, nullptr, port); | 179 | if (ec) { |
760 | 111 | svrsock_ = SDLNet_TCP_Open(&myaddr); | 180 | log("[NetHost] Error when sending to a client, closing connection: %s.\n", ec.message().c_str()); |
761 | 112 | // Maximal 16 sockets! This mean we can have at most 15 clients_ in our game (+ metaserver) | 181 | close(id); |
762 | 113 | sockset_ = SDLNet_AllocSocketSet(16); | 182 | } |
763 | 183 | } | ||
764 | 184 | } | ||
765 | 185 | |||
766 | 186 | NetHost::NetHost(const uint16_t port) | ||
767 | 187 | : clients_(), next_id_(1), io_service_(), acceptor_v4_(io_service_), acceptor_v6_(io_service_) { | ||
768 | 188 | |||
769 | 189 | if (open_acceptor(&acceptor_v4_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))) { | ||
770 | 190 | log("[NetHost]: Opening a listening IPv4 socket on TCP port %u\n", port); | ||
771 | 191 | } | ||
772 | 192 | if (open_acceptor(&acceptor_v6_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), port))) { | ||
773 | 193 | log("[NetHost]: Opening a listening IPv6 socket on TCP port %u\n", port); | ||
774 | 194 | } | ||
775 | 195 | } | ||
776 | 196 | |||
777 | 197 | bool NetHost::open_acceptor(boost::asio::ip::tcp::acceptor *acceptor, | ||
778 | 198 | const boost::asio::ip::tcp::endpoint& endpoint) { | ||
779 | 199 | try { | ||
780 | 200 | acceptor->open(endpoint.protocol()); | ||
781 | 201 | acceptor->non_blocking(true); | ||
782 | 202 | const boost::asio::socket_base::reuse_address option_reuse(true); | ||
783 | 203 | acceptor->set_option(option_reuse); | ||
784 | 204 | if (endpoint.protocol() == boost::asio::ip::tcp::v6()) { | ||
785 | 205 | const boost::asio::ip::v6_only option_v6only(true); | ||
786 | 206 | acceptor->set_option(option_v6only); | ||
787 | 207 | } | ||
788 | 208 | acceptor->bind(endpoint); | ||
789 | 209 | acceptor->listen(boost::asio::socket_base::max_connections); | ||
790 | 210 | return true; | ||
791 | 211 | } catch (const boost::system::system_error&) { | ||
792 | 212 | return false; | ||
793 | 213 | } | ||
794 | 114 | } | 214 | } |
795 | 115 | 215 | ||
796 | === modified file 'src/network/nethost.h' | |||
797 | --- src/network/nethost.h 2017-05-11 10:45:44 +0000 | |||
798 | +++ src/network/nethost.h 2017-06-07 18:50:40 +0000 | |||
799 | @@ -23,22 +23,22 @@ | |||
800 | 23 | #include <map> | 23 | #include <map> |
801 | 24 | #include <memory> | 24 | #include <memory> |
802 | 25 | 25 | ||
803 | 26 | #include <SDL_net.h> | ||
804 | 27 | |||
805 | 28 | #include "network/network.h" | 26 | #include "network/network.h" |
806 | 29 | 27 | ||
807 | 30 | /** | 28 | /** |
808 | 31 | * NetHost manages the client connections of a network game in which this computer | 29 | * NetHost manages the client connections of a network game in which this computer |
809 | 32 | * participates as a server. | 30 | * participates as a server. |
810 | 31 | * This class tries to create sockets for IPv4 and IPv6. | ||
811 | 33 | */ | 32 | */ |
812 | 34 | class NetHost { | 33 | class NetHost { |
813 | 35 | public: | 34 | public: |
814 | 35 | /// IDs used to enumerate the clients. | ||
815 | 36 | using ConnectionId = uint32_t; | 36 | using ConnectionId = uint32_t; |
816 | 37 | 37 | ||
817 | 38 | /** | 38 | /** |
818 | 39 | * Tries to listen on the given port. | 39 | * Tries to listen on the given port. |
821 | 40 | * @param port The port to listen on. | 40 | * \param port The port to listen on. |
822 | 41 | * @return A pointer to a listening \c NetHost object or a nullptr if the connection failed. | 41 | * \return A pointer to a listening \c NetHost object or a nullptr if the connection failed. |
823 | 42 | */ | 42 | */ |
824 | 43 | static std::unique_ptr<NetHost> listen(const uint16_t port); | 43 | static std::unique_ptr<NetHost> listen(const uint16_t port); |
825 | 44 | 44 | ||
826 | @@ -49,14 +49,14 @@ | |||
827 | 49 | 49 | ||
828 | 50 | /** | 50 | /** |
829 | 51 | * Returns whether the server is started and is listening. | 51 | * Returns whether the server is started and is listening. |
831 | 52 | * @return \c true if the server is listening, \c false otherwise. | 52 | * \return \c true if the server is listening, \c false otherwise. |
832 | 53 | */ | 53 | */ |
833 | 54 | bool is_listening() const; | 54 | bool is_listening() const; |
834 | 55 | 55 | ||
835 | 56 | /** | 56 | /** |
836 | 57 | * Returns whether the given client is connected. | 57 | * Returns whether the given client is connected. |
839 | 58 | * @param The id of the client to check. | 58 | * \param The id of the client to check. |
840 | 59 | * @return \c true if the connection is open, \c false otherwise. | 59 | * \return \c true if the connection is open, \c false otherwise. |
841 | 60 | */ | 60 | */ |
842 | 61 | bool is_connected(ConnectionId id) const; | 61 | bool is_connected(ConnectionId id) const; |
843 | 62 | 62 | ||
844 | @@ -67,14 +67,14 @@ | |||
845 | 67 | 67 | ||
846 | 68 | /** | 68 | /** |
847 | 69 | * Closes the connection to the given client. | 69 | * Closes the connection to the given client. |
849 | 70 | * @param id The id of the client to close the connection to. | 70 | * \param id The id of the client to close the connection to. |
850 | 71 | */ | 71 | */ |
851 | 72 | void close(ConnectionId id); | 72 | void close(ConnectionId id); |
852 | 73 | 73 | ||
853 | 74 | /** | 74 | /** |
854 | 75 | * Tries to accept a new client. | 75 | * Tries to accept a new client. |
857 | 76 | * @param new_id The connection id of the new client will be stored here. | 76 | * \param new_id The connection id of the new client will be stored here. |
858 | 77 | * @return \c true if a client has connected, \c false otherwise. | 77 | * \return \c true if a client has connected, \c false otherwise. |
859 | 78 | * The given id is only modified when \c true is returned. | 78 | * The given id is only modified when \c true is returned. |
860 | 79 | * Calling this on a closed server will return false. | 79 | * Calling this on a closed server will return false. |
861 | 80 | * The returned id is always greater than 0. | 80 | * The returned id is always greater than 0. |
862 | @@ -83,9 +83,9 @@ | |||
863 | 83 | 83 | ||
864 | 84 | /** | 84 | /** |
865 | 85 | * Tries to receive a packet. | 85 | * Tries to receive a packet. |
869 | 86 | * @param id The connection id of the client that should be received. | 86 | * \param id The connection id of the client that should be received. |
870 | 87 | * @param packet A packet that should be overwritten with the received data. | 87 | * \param packet A packet that should be overwritten with the received data. |
871 | 88 | * @return \c true if a packet is available, \c false otherwise. | 88 | * \return \c true if a packet is available, \c false otherwise. |
872 | 89 | * The given packet is only modified when \c true is returned. | 89 | * The given packet is only modified when \c true is returned. |
873 | 90 | * Calling this on a closed connection will return false. | 90 | * Calling this on a closed connection will return false. |
874 | 91 | */ | 91 | */ |
875 | @@ -94,26 +94,50 @@ | |||
876 | 94 | /** | 94 | /** |
877 | 95 | * Sends a packet. | 95 | * Sends a packet. |
878 | 96 | * Calling this on a closed connection will silently fail. | 96 | * Calling this on a closed connection will silently fail. |
881 | 97 | * @param id The connection id of the client that should be sent to. | 97 | * \param id The connection id of the client that should be sent to. |
882 | 98 | * @param packet The packet to send. | 98 | * \param packet The packet to send. |
883 | 99 | */ | 99 | */ |
884 | 100 | void send(ConnectionId id, const SendPacket& packet); | 100 | void send(ConnectionId id, const SendPacket& packet); |
885 | 101 | 101 | ||
886 | 102 | private: | 102 | private: |
887 | 103 | /** | ||
888 | 104 | * Tries to listen on the given port. | ||
889 | 105 | * If it fails, is_listening() will return \c false. | ||
890 | 106 | * \param port The port to listen on. | ||
891 | 107 | */ | ||
892 | 103 | NetHost(const uint16_t port); | 108 | NetHost(const uint16_t port); |
893 | 104 | 109 | ||
899 | 105 | class Client { | 110 | bool open_acceptor(boost::asio::ip::tcp::acceptor *acceptor, |
900 | 106 | public: | 111 | const boost::asio::ip::tcp::endpoint& endpoint); |
901 | 107 | Client(TCPsocket sock); | 112 | |
902 | 108 | 113 | /** | |
903 | 109 | TCPsocket socket; | 114 | * Helper structure to store variables about a connected client. |
904 | 115 | */ | ||
905 | 116 | struct Client { | ||
906 | 117 | /** | ||
907 | 118 | * Initializes the structure with the given socket. | ||
908 | 119 | * \param sock The socket to listen on. The socket is moved by this | ||
909 | 120 | * constructor so the given socket is no longer valid. | ||
910 | 121 | */ | ||
911 | 122 | Client(boost::asio::ip::tcp::socket&& sock); | ||
912 | 123 | |||
913 | 124 | /// The socket to send/receive with. | ||
914 | 125 | boost::asio::ip::tcp::socket socket; | ||
915 | 126 | /// The deserializer to feed the received data to. It will transform it into data packets. | ||
916 | 110 | Deserializer deserializer; | 127 | Deserializer deserializer; |
917 | 111 | }; | 128 | }; |
918 | 112 | 129 | ||
921 | 113 | TCPsocket svrsock_; | 130 | /// A map linking client ids to the respective data about the clients. |
922 | 114 | SDLNet_SocketSet sockset_; | 131 | /// Client ids not in this map should be considered invalid. |
923 | 115 | std::map<NetHost::ConnectionId, Client> clients_; | 132 | std::map<NetHost::ConnectionId, Client> clients_; |
924 | 133 | /// The next client id that will be used | ||
925 | 116 | NetHost::ConnectionId next_id_; | 134 | NetHost::ConnectionId next_id_; |
926 | 135 | /// An io_service needed by boost.asio. Primary needed for async operations. | ||
927 | 136 | boost::asio::io_service io_service_; | ||
928 | 137 | /// The acceptor we get IPv4 connection requests to. | ||
929 | 138 | boost::asio::ip::tcp::acceptor acceptor_v4_; | ||
930 | 139 | /// The acceptor we get IPv6 connection requests to. | ||
931 | 140 | boost::asio::ip::tcp::acceptor acceptor_v6_; | ||
932 | 117 | }; | 141 | }; |
933 | 118 | 142 | ||
934 | 119 | #endif // end of include guard: WL_NETWORK_NETHOST_H | 143 | #endif // end of include guard: WL_NETWORK_NETHOST_H |
935 | 120 | 144 | ||
936 | === modified file 'src/network/network.cc' | |||
937 | --- src/network/network.cc 2017-05-14 14:40:24 +0000 | |||
938 | +++ src/network/network.cc 2017-06-07 18:50:40 +0000 | |||
939 | @@ -19,8 +19,62 @@ | |||
940 | 19 | 19 | ||
941 | 20 | #include "network/network.h" | 20 | #include "network/network.h" |
942 | 21 | 21 | ||
943 | 22 | #include <SDL.h> | ||
944 | 23 | |||
945 | 22 | #include "base/log.h" | 24 | #include "base/log.h" |
946 | 23 | 25 | ||
947 | 26 | |||
948 | 27 | namespace { | ||
949 | 28 | |||
950 | 29 | bool do_resolve(const boost::asio::ip::tcp& protocol, NetAddress *addr, const std::string& hostname, uint16_t port) { | ||
951 | 30 | assert(addr != nullptr); | ||
952 | 31 | try { | ||
953 | 32 | boost::asio::io_service io_service; | ||
954 | 33 | boost::asio::ip::tcp::resolver resolver(io_service); | ||
955 | 34 | boost::asio::ip::tcp::resolver::query query(protocol, hostname, boost::lexical_cast<std::string>(port)); | ||
956 | 35 | boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query); | ||
957 | 36 | if (iter == boost::asio::ip::tcp::resolver::iterator()) { | ||
958 | 37 | // Resolution failed | ||
959 | 38 | return false; | ||
960 | 39 | } | ||
961 | 40 | addr->ip = iter->endpoint().address(); | ||
962 | 41 | addr->port = port; | ||
963 | 42 | return true; | ||
964 | 43 | } catch (const boost::system::system_error& ec) { | ||
965 | 44 | // Resolution failed | ||
966 | 45 | log("Could not resolve network name: %s", ec.what()); | ||
967 | 46 | return false; | ||
968 | 47 | } | ||
969 | 48 | } | ||
970 | 49 | |||
971 | 50 | } | ||
972 | 51 | |||
973 | 52 | bool NetAddress::resolve_to_v4(NetAddress *addr, const std::string& hostname, uint16_t port) { | ||
974 | 53 | return do_resolve(boost::asio::ip::tcp::v4(), addr, hostname, port); | ||
975 | 54 | } | ||
976 | 55 | |||
977 | 56 | bool NetAddress::resolve_to_v6(NetAddress *addr, const std::string& hostname, uint16_t port) { | ||
978 | 57 | return do_resolve(boost::asio::ip::tcp::v6(), addr, hostname, port); | ||
979 | 58 | } | ||
980 | 59 | |||
981 | 60 | bool NetAddress::parse_ip(NetAddress *addr, const std::string& ip, uint16_t port) { | ||
982 | 61 | boost::system::error_code ec; | ||
983 | 62 | boost::asio::ip::address new_addr = boost::asio::ip::address::from_string(ip, ec); | ||
984 | 63 | if (ec) | ||
985 | 64 | return false; | ||
986 | 65 | addr->ip = new_addr; | ||
987 | 66 | addr->port = port; | ||
988 | 67 | return true; | ||
989 | 68 | } | ||
990 | 69 | |||
991 | 70 | bool NetAddress::is_ipv6() const { | ||
992 | 71 | return ip.is_v6(); | ||
993 | 72 | } | ||
994 | 73 | |||
995 | 74 | bool NetAddress::is_valid() const { | ||
996 | 75 | return port != 0 && !ip.is_unspecified(); | ||
997 | 76 | } | ||
998 | 77 | |||
999 | 24 | CmdNetCheckSync::CmdNetCheckSync(uint32_t const dt, SyncCallback* const cb) | 78 | CmdNetCheckSync::CmdNetCheckSync(uint32_t const dt, SyncCallback* const cb) |
1000 | 25 | : Command(dt), callback_(cb) { | 79 | : Command(dt), callback_(cb) { |
1001 | 26 | } | 80 | } |
1002 | @@ -184,7 +238,7 @@ | |||
1003 | 184 | } | 238 | } |
1004 | 185 | 239 | ||
1005 | 186 | DisconnectException::DisconnectException(const char* fmt, ...) { | 240 | DisconnectException::DisconnectException(const char* fmt, ...) { |
1007 | 187 | char buffer[512]; | 241 | char buffer[kNetworkBufferSize]; |
1008 | 188 | { | 242 | { |
1009 | 189 | va_list va; | 243 | va_list va; |
1010 | 190 | va_start(va, fmt); | 244 | va_start(va, fmt); |
1011 | 191 | 245 | ||
1012 | === modified file 'src/network/network.h' | |||
1013 | --- src/network/network.h 2017-05-23 21:07:47 +0000 | |||
1014 | +++ src/network/network.h 2017-06-07 18:50:40 +0000 | |||
1015 | @@ -24,7 +24,7 @@ | |||
1016 | 24 | #include <string> | 24 | #include <string> |
1017 | 25 | #include <vector> | 25 | #include <vector> |
1018 | 26 | 26 | ||
1020 | 27 | #include <SDL_net.h> | 27 | #include <boost/asio.hpp> |
1021 | 28 | #include <boost/lexical_cast.hpp> | 28 | #include <boost/lexical_cast.hpp> |
1022 | 29 | 29 | ||
1023 | 30 | #include "base/wexception.h" | 30 | #include "base/wexception.h" |
1024 | @@ -36,6 +36,60 @@ | |||
1025 | 36 | class Deserializer; | 36 | class Deserializer; |
1026 | 37 | class FileRead; | 37 | class FileRead; |
1027 | 38 | 38 | ||
1028 | 39 | constexpr size_t kNetworkBufferSize = 512; | ||
1029 | 40 | |||
1030 | 41 | /** | ||
1031 | 42 | * Simple structure to hold the IP address and port of a server. | ||
1032 | 43 | * This structure must not contain a hostname but only IP addresses. | ||
1033 | 44 | */ | ||
1034 | 45 | struct NetAddress { | ||
1035 | 46 | /** | ||
1036 | 47 | * Tries to resolve the given hostname to an IPv4 address. | ||
1037 | 48 | * \param[out] addr A NetAddress structure to write the result to, | ||
1038 | 49 | * if resolution succeeds. | ||
1039 | 50 | * \param hostname The name of the host. | ||
1040 | 51 | * \param port The port on the host. | ||
1041 | 52 | * \return \c True if the resolution succeeded, \c false otherwise. | ||
1042 | 53 | */ | ||
1043 | 54 | static bool resolve_to_v4(NetAddress *addr, const std::string& hostname, uint16_t port); | ||
1044 | 55 | |||
1045 | 56 | /** | ||
1046 | 57 | * Tries to resolve the given hostname to an IPv6 address. | ||
1047 | 58 | * \param[out] addr A NetAddress structure to write the result to, | ||
1048 | 59 | * if resolution succeeds. | ||
1049 | 60 | * \param hostname The name of the host. | ||
1050 | 61 | * \param port The port on the host. | ||
1051 | 62 | * \return \c True if the resolution succeeded, \c false otherwise. | ||
1052 | 63 | */ | ||
1053 | 64 | static bool resolve_to_v6(NetAddress *addr, const std::string& hostname, uint16_t port); | ||
1054 | 65 | |||
1055 | 66 | /** | ||
1056 | 67 | * Parses the given string to an IP address. | ||
1057 | 68 | * \param[out] addr A NetAddress structure to write the result to, | ||
1058 | 69 | * if parsing succeeds. | ||
1059 | 70 | * \param ip An IP address as string. | ||
1060 | 71 | * \param port The port on the host. | ||
1061 | 72 | * \return \c True if the parsing succeeded, \c false otherwise. | ||
1062 | 73 | */ | ||
1063 | 74 | static bool parse_ip(NetAddress *addr, const std::string& ip, uint16_t port); | ||
1064 | 75 | |||
1065 | 76 | /** | ||
1066 | 77 | * Returns whether the stored IP is in IPv6 format. | ||
1067 | 78 | * @return \c true if the stored IP is in IPv6 format, \c false otherwise. | ||
1068 | 79 | * If it isn't an IPv6 address, it is an IPv4 address. | ||
1069 | 80 | */ | ||
1070 | 81 | bool is_ipv6() const; | ||
1071 | 82 | |||
1072 | 83 | /** | ||
1073 | 84 | * Returns whether valid IP address and port are stored. | ||
1074 | 85 | * @return \c true if valid, \c false otherwise. | ||
1075 | 86 | */ | ||
1076 | 87 | bool is_valid() const; | ||
1077 | 88 | |||
1078 | 89 | boost::asio::ip::address ip; | ||
1079 | 90 | uint16_t port; | ||
1080 | 91 | }; | ||
1081 | 92 | |||
1082 | 39 | struct SyncCallback { | 93 | struct SyncCallback { |
1083 | 40 | virtual ~SyncCallback() { | 94 | virtual ~SyncCallback() { |
1084 | 41 | } | 95 | } |
1085 | 42 | 96 | ||
1086 | === modified file 'src/network/network_lan_promotion.cc' | |||
1087 | --- src/network/network_lan_promotion.cc 2017-01-25 18:55:59 +0000 | |||
1088 | +++ src/network/network_lan_promotion.cc 2017-06-07 18:50:40 +0000 | |||
1089 | @@ -19,116 +19,349 @@ | |||
1090 | 19 | 19 | ||
1091 | 20 | #include "network/network_lan_promotion.h" | 20 | #include "network/network_lan_promotion.h" |
1092 | 21 | 21 | ||
1095 | 22 | #include <cstdio> | 22 | #ifndef _WIN32 |
1096 | 23 | #include <cstring> | 23 | #include <ifaddrs.h> |
1097 | 24 | #endif | ||
1098 | 24 | 25 | ||
1099 | 26 | #include "base/i18n.h" | ||
1100 | 25 | #include "base/log.h" | 27 | #include "base/log.h" |
1102 | 26 | #include "base/macros.h" | 28 | #include "base/warning.h" |
1103 | 27 | #include "build_info.h" | 29 | #include "build_info.h" |
1104 | 28 | #include "network/constants.h" | 30 | #include "network/constants.h" |
1105 | 29 | 31 | ||
1106 | 32 | namespace { | ||
1107 | 33 | |||
1108 | 34 | /** | ||
1109 | 35 | * Returns the IP version. | ||
1110 | 36 | * \param addr The address object to get the IP version for. | ||
1111 | 37 | * \return Either 4 or 6, depending on the version of the given address. | ||
1112 | 38 | */ | ||
1113 | 39 | int get_ip_version(const boost::asio::ip::address& addr) { | ||
1114 | 40 | assert(!addr.is_unspecified()); | ||
1115 | 41 | if (addr.is_v4()) { | ||
1116 | 42 | return 4; | ||
1117 | 43 | } else { | ||
1118 | 44 | assert(addr.is_v6()); | ||
1119 | 45 | return 6; | ||
1120 | 46 | } | ||
1121 | 47 | } | ||
1122 | 48 | |||
1123 | 49 | /** | ||
1124 | 50 | * Returns the IP version. | ||
1125 | 51 | * \param version A whatever object to get the IP version for. | ||
1126 | 52 | * \return Either 4 or 6, depending on the version of the given address. | ||
1127 | 53 | */ | ||
1128 | 54 | int get_ip_version(const boost::asio::ip::udp& version) { | ||
1129 | 55 | if (version == boost::asio::ip::udp::v4()) { | ||
1130 | 56 | return 4; | ||
1131 | 57 | } else { | ||
1132 | 58 | assert(version == boost::asio::ip::udp::v6()); | ||
1133 | 59 | return 6; | ||
1134 | 60 | } | ||
1135 | 61 | } | ||
1136 | 62 | } | ||
1137 | 63 | |||
1138 | 30 | /*** class LanBase ***/ | 64 | /*** class LanBase ***/ |
1147 | 31 | 65 | /** | |
1148 | 32 | LanBase::LanBase() { | 66 | * \internal |
1149 | 33 | 67 | * In an ideal world, we would use the same code with boost asio for all three operating systems. | |
1150 | 34 | sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); // open the socket | 68 | * Unfortunately, it isn't that easy and we need some platform specific code. |
1151 | 35 | 69 | * For IPv4, windows needs a special case: For Linux and Apple we have to iterate over all assigned IPv4 | |
1152 | 36 | int32_t opt = 1; | 70 | * addresses (e.g. 192.168.1.68), transform them to broadcast addresses (e.g. 192.168.1.255) and send our |
1153 | 37 | // the cast to char* is because microsoft wants it that way | 71 | * packets to those addresses. For windows, we simply can send to 255.255.255.255. |
1154 | 38 | setsockopt(sock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&opt), sizeof(opt)); | 72 | * For IPv6, Apple requires special handling. On the other two operating systems we can send to the multicast |
1155 | 73 | * address ff02::1 (kind of a local broadcast) without specifying over which interface we want to send. | ||
1156 | 74 | * On Apple we have to specify the interface, forcing us to send our message over all interfaces we can find. | ||
1157 | 75 | */ | ||
1158 | 76 | LanBase::LanBase(uint16_t port) | ||
1159 | 77 | : io_service(), socket_v4(io_service), socket_v6(io_service) { | ||
1160 | 39 | 78 | ||
1161 | 40 | #ifndef _WIN32 | 79 | #ifndef _WIN32 |
1186 | 41 | 80 | // Iterate over all interfaces. If they support IPv4, store the broadcast-address | |
1187 | 42 | // get a list of all local broadcast addresses | 81 | // of the interface and try to start the socket. If they support IPv6, just start |
1188 | 43 | struct if_nameindex* ifnames = if_nameindex(); | 82 | // the socket. There is one fixed broadcast-address for IPv6 (well, actually multicast) |
1189 | 44 | struct ifreq ifr; | 83 | |
1190 | 45 | 84 | // Adapted example out of "man getifaddrs" | |
1191 | 46 | for (int32_t i = 0; ifnames[i].if_index; ++i) { | 85 | // TODO(Notabilis): I don't like this part. But boost is not able to iterate over |
1192 | 47 | strncpy(ifr.ifr_name, ifnames[i].if_name, IFNAMSIZ); | 86 | // the local IPs and interfaces at this time. If they ever add it, replace this code |
1193 | 48 | 87 | struct ifaddrs *ifaddr, *ifa; | |
1194 | 49 | DIAG_OFF("-Wold-style-cast") | 88 | int s, n; |
1195 | 50 | if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) | 89 | char host[NI_MAXHOST]; |
1196 | 51 | continue; | 90 | if (getifaddrs(&ifaddr) == -1) { |
1197 | 52 | 91 | perror("getifaddrs"); | |
1198 | 53 | if (!(ifr.ifr_flags & IFF_BROADCAST)) | 92 | exit(EXIT_FAILURE); |
1199 | 54 | continue; | 93 | } |
1200 | 55 | 94 | for (ifa = ifaddr, n = 0; ifa != nullptr; ifa = ifa->ifa_next, n++) { | |
1201 | 56 | if (ioctl(sock, SIOCGIFBRDADDR, &ifr) < 0) | 95 | if (ifa->ifa_addr == nullptr) |
1202 | 57 | continue; | 96 | continue; |
1203 | 58 | DIAG_ON("-Wold-style-cast") | 97 | if (!(ifa->ifa_flags & IFF_BROADCAST) && !(ifa->ifa_flags & IFF_MULTICAST)) |
1204 | 59 | 98 | continue; | |
1205 | 60 | broadcast_addresses.push_back( | 99 | switch (ifa->ifa_addr->sa_family) { |
1206 | 61 | reinterpret_cast<sockaddr_in*>(&ifr.ifr_broadaddr)->sin_addr.s_addr); | 100 | case AF_INET: |
1207 | 62 | } | 101 | s = getnameinfo(ifa->ifa_broadaddr, sizeof(struct sockaddr_in), |
1208 | 63 | 102 | host, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST); | |
1209 | 64 | if_freenameindex(ifnames); | 103 | if (s == 0) { |
1210 | 104 | start_socket(&socket_v4, boost::asio::ip::udp::v4(), port); | ||
1211 | 105 | broadcast_addresses_v4.insert(host); | ||
1212 | 106 | } | ||
1213 | 107 | break; | ||
1214 | 108 | case AF_INET6: | ||
1215 | 109 | #ifdef __APPLE__ | ||
1216 | 110 | interface_indices_v6.insert(if_nametoindex(ifa->ifa_name)); | ||
1217 | 111 | #endif | ||
1218 | 112 | start_socket(&socket_v6, boost::asio::ip::udp::v6(), port); | ||
1219 | 113 | // No address to store here. There is only one "broadcast" address for IPv6 | ||
1220 | 114 | break; | ||
1221 | 115 | } | ||
1222 | 116 | } | ||
1223 | 117 | freeifaddrs(ifaddr); | ||
1224 | 118 | |||
1225 | 65 | #else | 119 | #else |
1226 | 66 | // As Microsoft does not seem to support if_nameindex, we just broadcast to | 120 | // As Microsoft does not seem to support if_nameindex, we just broadcast to |
1227 | 67 | // INADDR_BROADCAST. | 121 | // INADDR_BROADCAST. |
1229 | 68 | broadcast_addresses.push_back(INADDR_BROADCAST); | 122 | broadcast_addresses_v4.insert("255.255.255.255"); |
1230 | 69 | #endif | 123 | #endif |
1231 | 124 | |||
1232 | 125 | if (!is_open()) { | ||
1233 | 126 | // Hm, not good. Just try to open them and hope for the best | ||
1234 | 127 | log("[LAN] Trying to open both sockets.\n"); | ||
1235 | 128 | start_socket(&socket_v4, boost::asio::ip::udp::v4(), port); | ||
1236 | 129 | start_socket(&socket_v6, boost::asio::ip::udp::v6(), port); | ||
1237 | 130 | } | ||
1238 | 131 | |||
1239 | 132 | if (!is_open()) { | ||
1240 | 133 | // Still not open? Go back to main menu. | ||
1241 | 134 | log("[LAN] Error: No sockets could be opened.\n"); | ||
1242 | 135 | report_network_error(); | ||
1243 | 136 | } | ||
1244 | 137 | |||
1245 | 138 | for (const std::string& ip : broadcast_addresses_v4) | ||
1246 | 139 | log("[LAN] Will broadcast to %s.\n", ip.c_str()); | ||
1247 | 140 | if (socket_v6.is_open()) | ||
1248 | 141 | log("[LAN] Will broadcast for IPv6.\n"); | ||
1249 | 70 | } | 142 | } |
1250 | 71 | 143 | ||
1251 | 72 | LanBase::~LanBase() { | 144 | LanBase::~LanBase() { |
1304 | 73 | closesocket(sock); | 145 | close_socket(&socket_v4); |
1305 | 74 | } | 146 | close_socket(&socket_v6); |
1306 | 75 | 147 | } | |
1307 | 76 | void LanBase::bind(uint16_t port) { | 148 | |
1308 | 77 | sockaddr_in addr; | 149 | bool LanBase::is_available() { |
1309 | 78 | 150 | const auto do_is_available = [this](boost::asio::ip::udp::socket& socket) -> bool { | |
1310 | 79 | DIAG_OFF("-Wold-style-cast") | 151 | boost::system::error_code ec; |
1311 | 80 | addr.sin_family = AF_INET; | 152 | bool available = (socket.is_open() && socket.available(ec) > 0); |
1312 | 81 | addr.sin_addr.s_addr = INADDR_ANY; | 153 | if (ec) { |
1313 | 82 | addr.sin_port = htons(port); | 154 | log("[LAN] Error when checking whether data is available on IPv%d socket, closing it: %s.\n", |
1314 | 83 | DIAG_ON("-Wold-style-cast") | 155 | get_ip_version(socket.local_endpoint().protocol()), ec.message().c_str()); |
1315 | 84 | 156 | close_socket(&socket); | |
1316 | 85 | ::bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)); | 157 | return false; |
1317 | 86 | } | 158 | } |
1318 | 87 | 159 | return available; | |
1319 | 88 | bool LanBase::avail() { | 160 | }; |
1320 | 89 | fd_set fds; | 161 | |
1321 | 90 | timeval tv; | 162 | return do_is_available(socket_v4) || do_is_available(socket_v6); |
1322 | 91 | 163 | } | |
1323 | 92 | DIAG_OFF("-Wold-style-cast") | 164 | |
1324 | 93 | FD_ZERO(&fds); | 165 | bool LanBase::is_open() { |
1325 | 94 | FD_SET(sock, &fds); | 166 | return socket_v4.is_open() || socket_v6.is_open(); |
1326 | 95 | DIAG_ON("-Wold-style-cast") | 167 | } |
1327 | 96 | 168 | ||
1328 | 97 | tv.tv_sec = 0; | 169 | ssize_t LanBase::receive(void* const buf, size_t const len, NetAddress *addr) { |
1329 | 98 | tv.tv_usec = 0; | 170 | assert(buf != nullptr); |
1330 | 99 | 171 | assert(addr != nullptr); | |
1331 | 100 | return select(sock + 1, &fds, nullptr, nullptr, &tv) == 1; | 172 | size_t recv_len = 0; |
1332 | 101 | } | 173 | |
1333 | 102 | 174 | const auto do_receive | |
1334 | 103 | ssize_t LanBase::receive(void* const buf, size_t const len, sockaddr_in* const addr) { | 175 | = [this, &buf, &len, &recv_len, addr](boost::asio::ip::udp::socket& socket) -> bool { |
1335 | 104 | socklen_t addrlen = sizeof(sockaddr_in); | 176 | if (socket.is_open()) { |
1336 | 105 | return recvfrom( | 177 | try { |
1337 | 106 | sock, static_cast<DATATYPE*>(buf), len, 0, reinterpret_cast<sockaddr*>(addr), &addrlen); | 178 | if (socket.available() > 0) { |
1338 | 107 | } | 179 | boost::asio::ip::udp::endpoint sender_endpoint; |
1339 | 108 | 180 | recv_len = socket.receive_from(boost::asio::buffer(buf, len), sender_endpoint); | |
1340 | 109 | void LanBase::send(void const* const buf, size_t const len, sockaddr_in const* const addr) { | 181 | *addr = NetAddress{sender_endpoint.address(), sender_endpoint.port()}; |
1341 | 110 | sendto(sock, static_cast<const DATATYPE*>(buf), len, 0, reinterpret_cast<const sockaddr*>(addr), | 182 | assert(recv_len <= len); |
1342 | 111 | sizeof(sockaddr_in)); | 183 | return true; |
1343 | 112 | } | 184 | } |
1344 | 113 | 185 | } catch (const boost::system::system_error& ec) { | |
1345 | 114 | void LanBase::broadcast(void const* const buf, size_t const len, uint16_t const port) { | 186 | // Some network error. Close the socket |
1346 | 115 | for (const in_addr_t& temp_address : broadcast_addresses) { | 187 | log("[LAN] Error when receiving data on IPv%d socket, closing it: %s.\n", |
1347 | 116 | sockaddr_in addr; | 188 | get_ip_version(socket.local_endpoint().protocol()), ec.what()); |
1348 | 117 | addr.sin_family = AF_INET; | 189 | close_socket(&socket); |
1349 | 118 | addr.sin_addr.s_addr = temp_address; | 190 | } |
1350 | 119 | DIAG_OFF("-Wold-style-cast") | 191 | } |
1351 | 120 | addr.sin_port = htons(port); | 192 | // Nothing received |
1352 | 121 | DIAG_ON("-Wold-style-cast") | 193 | return false; |
1353 | 122 | 194 | }; | |
1354 | 123 | sendto(sock, static_cast<const DATATYPE*>(buf), len, 0, | 195 | |
1355 | 124 | reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)); | 196 | // Try to receive something somewhere |
1356 | 197 | if (!do_receive(socket_v4)) | ||
1357 | 198 | do_receive(socket_v6); | ||
1358 | 199 | |||
1359 | 200 | // Return how much has been received, might be 0 | ||
1360 | 201 | return recv_len; | ||
1361 | 202 | } | ||
1362 | 203 | |||
1363 | 204 | bool LanBase::send(void const* const buf, size_t const len, const NetAddress& addr) { | ||
1364 | 205 | boost::system::error_code ec; | ||
1365 | 206 | assert(addr.is_valid()); | ||
1366 | 207 | // If this assert failed, then there is some bug in the code. NetAddress should only be filled | ||
1367 | 208 | // with valid IP addresses (e.g. no hostnames) | ||
1368 | 209 | assert(!ec); | ||
1369 | 210 | boost::asio::ip::udp::endpoint destination(addr.ip, addr.port); | ||
1370 | 211 | boost::asio::ip::udp::socket *socket = nullptr; | ||
1371 | 212 | if (destination.address().is_v4()) { | ||
1372 | 213 | socket = &socket_v4; | ||
1373 | 214 | } else if (destination.address().is_v6()) { | ||
1374 | 215 | socket = &socket_v6; | ||
1375 | 216 | } else { | ||
1376 | 217 | NEVER_HERE(); | ||
1377 | 218 | } | ||
1378 | 219 | assert(socket != nullptr); | ||
1379 | 220 | if (!socket->is_open()) { | ||
1380 | 221 | // I think this shouldn't happen normally. It might happen, though, if we receive | ||
1381 | 222 | // a broadcast and learn the IP, then our sockets goes down, then we try to send | ||
1382 | 223 | log("[LAN] Error: trying to send to an IPv%d address but socket is not open.\n", | ||
1383 | 224 | get_ip_version(addr.ip)); | ||
1384 | 225 | return false; | ||
1385 | 226 | } | ||
1386 | 227 | socket->send_to(boost::asio::buffer(buf, len), destination, 0, ec); | ||
1387 | 228 | if (ec) { | ||
1388 | 229 | log("[LAN] Error when trying to send something over IPv%d, closing socket: %s.\n", | ||
1389 | 230 | get_ip_version(addr.ip), ec.message().c_str()); | ||
1390 | 231 | close_socket(socket); | ||
1391 | 232 | return false; | ||
1392 | 233 | } | ||
1393 | 234 | return true; | ||
1394 | 235 | } | ||
1395 | 236 | |||
1396 | 237 | bool LanBase::broadcast(void const* const buf, size_t const len, uint16_t const port) { | ||
1397 | 238 | |||
1398 | 239 | const auto do_broadcast | ||
1399 | 240 | = [this, buf, len, port](boost::asio::ip::udp::socket& socket, const std::string& address) -> bool { | ||
1400 | 241 | if (socket.is_open()) { | ||
1401 | 242 | boost::system::error_code ec; | ||
1402 | 243 | boost::asio::ip::udp::endpoint destination(boost::asio::ip::address::from_string(address), port); | ||
1403 | 244 | socket.send_to(boost::asio::buffer(buf, len), destination, 0, ec); | ||
1404 | 245 | if (!ec) { | ||
1405 | 246 | return true; | ||
1406 | 247 | } | ||
1407 | 248 | #ifdef __APPLE__ | ||
1408 | 249 | if (get_ip_version(destination.address()) == 4) { | ||
1409 | 250 | #endif // __APPLE__ | ||
1410 | 251 | log("[LAN] Error when broadcasting on IPv%d socket to %s, closing it: %s.\n", | ||
1411 | 252 | get_ip_version(destination.address()), address.c_str(), ec.message().c_str()); | ||
1412 | 253 | close_socket(&socket); | ||
1413 | 254 | #ifdef __APPLE__ | ||
1414 | 255 | } else { | ||
1415 | 256 | log("[LAN] Error when broadcasting on IPv6 socket to %s: %s.\n", | ||
1416 | 257 | address.c_str(), ec.message().c_str()); | ||
1417 | 258 | } | ||
1418 | 259 | #endif // __APPLE__ | ||
1419 | 260 | } | ||
1420 | 261 | return false; | ||
1421 | 262 | }; | ||
1422 | 263 | |||
1423 | 264 | bool one_success = false; | ||
1424 | 265 | |||
1425 | 266 | // IPv4 broadcasting is the same for all | ||
1426 | 267 | for (const std::string& address : broadcast_addresses_v4) { | ||
1427 | 268 | one_success |= do_broadcast(socket_v4, address); | ||
1428 | 269 | } | ||
1429 | 270 | #ifndef __APPLE__ | ||
1430 | 271 | // For IPv6 on Linux and Windows just send on an undefined network interface | ||
1431 | 272 | one_success |= do_broadcast(socket_v6, "ff02::1"); | ||
1432 | 273 | #else // __APPLE__ | ||
1433 | 274 | |||
1434 | 275 | // Apple forces us to define which interface we want to send through | ||
1435 | 276 | for (auto it = interface_indices_v6.begin(); it != interface_indices_v6.end(); ) { | ||
1436 | 277 | socket_v6.set_option(boost::asio::ip::multicast::outbound_interface(*it)); | ||
1437 | 278 | bool success = do_broadcast(socket_v6, "ff02::1"); | ||
1438 | 279 | one_success |= success; | ||
1439 | 280 | if (!success) { | ||
1440 | 281 | // Remove this interface id from the set | ||
1441 | 282 | it = interface_indices_v6.erase(it); | ||
1442 | 283 | if (interface_indices_v6.empty()) { | ||
1443 | 284 | log("[LAN] Warning: No more multicast capable IPv6 interfaces." | ||
1444 | 285 | "Other LAN players won't find your game.\n"); | ||
1445 | 286 | } | ||
1446 | 287 | } else { | ||
1447 | 288 | ++it; | ||
1448 | 289 | } | ||
1449 | 290 | } | ||
1450 | 291 | #endif // __APPLE__ | ||
1451 | 292 | return one_success; | ||
1452 | 293 | } | ||
1453 | 294 | |||
1454 | 295 | void LanBase::start_socket(boost::asio::ip::udp::socket *socket, boost::asio::ip::udp version, uint16_t port) { | ||
1455 | 296 | |||
1456 | 297 | if (socket->is_open()) | ||
1457 | 298 | return; | ||
1458 | 299 | |||
1459 | 300 | boost::system::error_code ec; | ||
1460 | 301 | // Try to open the socket | ||
1461 | 302 | socket->open(version, ec); | ||
1462 | 303 | if (ec) { | ||
1463 | 304 | log("[LAN] Failed to start an IPv%d socket: %s.\n", | ||
1464 | 305 | get_ip_version(version), ec.message().c_str()); | ||
1465 | 306 | return; | ||
1466 | 307 | } | ||
1467 | 308 | |||
1468 | 309 | const boost::asio::socket_base::broadcast option_broadcast(true); | ||
1469 | 310 | socket->set_option(option_broadcast, ec); | ||
1470 | 311 | if (ec) { | ||
1471 | 312 | log("[LAN] Error setting options for IPv%d socket, closing socket: %s.\n", | ||
1472 | 313 | get_ip_version(version), ec.message().c_str()); | ||
1473 | 314 | // Retrieve the error code to avoid throwing but ignore it | ||
1474 | 315 | close_socket(socket); | ||
1475 | 316 | return; | ||
1476 | 317 | } | ||
1477 | 318 | |||
1478 | 319 | const boost::asio::socket_base::reuse_address option_reuse(true); | ||
1479 | 320 | socket->set_option(option_reuse, ec); | ||
1480 | 321 | // This one isn't really needed so ignore the error | ||
1481 | 322 | |||
1482 | 323 | |||
1483 | 324 | if (version == boost::asio::ip::udp::v6()) { | ||
1484 | 325 | const boost::asio::ip::v6_only option_v6only(true); | ||
1485 | 326 | socket->set_option(option_v6only, ec); | ||
1486 | 327 | // This one might not be needed, ignore the error and see whether we fail on bind() | ||
1487 | 328 | } | ||
1488 | 329 | |||
1489 | 330 | socket->bind(boost::asio::ip::udp::endpoint(version, port), ec); | ||
1490 | 331 | if (ec) { | ||
1491 | 332 | log("[LAN] Error binding IPv%d socket to UDP port %d, closing socket: %s.\n", | ||
1492 | 333 | get_ip_version(version), port, ec.message().c_str()); | ||
1493 | 334 | close_socket(socket); | ||
1494 | 335 | return; | ||
1495 | 336 | } | ||
1496 | 337 | |||
1497 | 338 | log("[LAN] Started an IPv%d socket on UDP port %d.\n", get_ip_version(version), port); | ||
1498 | 339 | } | ||
1499 | 340 | |||
1500 | 341 | void LanBase::report_network_error() { | ||
1501 | 342 | // No socket open? Sorry, but we can't continue this way | ||
1502 | 343 | throw WLWarning(_("Failed to use the local network!"), | ||
1503 | 344 | _("Widelands was unable to use the local network. " | ||
1504 | 345 | "Maybe some other process is already running a server on port %d, %d or %d " | ||
1505 | 346 | "or your network setup is broken."), | ||
1506 | 347 | WIDELANDS_LAN_DISCOVERY_PORT, WIDELANDS_LAN_PROMOTION_PORT, WIDELANDS_PORT); | ||
1507 | 348 | } | ||
1508 | 349 | |||
1509 | 350 | void LanBase::close_socket(boost::asio::ip::udp::socket *socket) { | ||
1510 | 351 | boost::system::error_code ec; | ||
1511 | 352 | if (socket->is_open()) { | ||
1512 | 353 | const boost::asio::ip::udp::endpoint& endpoint = socket->local_endpoint(ec); | ||
1513 | 354 | if (!ec) | ||
1514 | 355 | log("[LAN] Closing an IPv%d socket.\n", get_ip_version(endpoint.protocol())); | ||
1515 | 356 | socket->shutdown(boost::asio::ip::udp::socket::shutdown_both, ec); | ||
1516 | 357 | socket->close(ec); | ||
1517 | 125 | } | 358 | } |
1518 | 126 | } | 359 | } |
1519 | 127 | 360 | ||
1520 | 128 | /*** class LanGamePromoter ***/ | 361 | /*** class LanGamePromoter ***/ |
1521 | 129 | 362 | ||
1524 | 130 | LanGamePromoter::LanGamePromoter() { | 363 | LanGamePromoter::LanGamePromoter() |
1525 | 131 | bind(WIDELANDS_LAN_PROMOTION_PORT); | 364 | : LanBase(WIDELANDS_LAN_PROMOTION_PORT) { |
1526 | 132 | 365 | ||
1527 | 133 | needupdate = true; | 366 | needupdate = true; |
1528 | 134 | 367 | ||
1529 | @@ -140,12 +373,13 @@ | |||
1530 | 140 | 373 | ||
1531 | 141 | strncpy(gameinfo.gameversion, build_id().c_str(), sizeof(gameinfo.gameversion)); | 374 | strncpy(gameinfo.gameversion, build_id().c_str(), sizeof(gameinfo.gameversion)); |
1532 | 142 | 375 | ||
1534 | 143 | gethostname(gameinfo.hostname, sizeof(gameinfo.hostname)); | 376 | strncpy(gameinfo.hostname, boost::asio::ip::host_name().c_str(), sizeof(gameinfo.hostname)); |
1535 | 144 | } | 377 | } |
1536 | 145 | 378 | ||
1537 | 146 | LanGamePromoter::~LanGamePromoter() { | 379 | LanGamePromoter::~LanGamePromoter() { |
1538 | 147 | gameinfo.state = LAN_GAME_CLOSED; | 380 | gameinfo.state = LAN_GAME_CLOSED; |
1539 | 148 | 381 | ||
1540 | 382 | // Don't care about errors at this point | ||
1541 | 149 | broadcast(&gameinfo, sizeof(gameinfo), WIDELANDS_LAN_DISCOVERY_PORT); | 383 | broadcast(&gameinfo, sizeof(gameinfo), WIDELANDS_LAN_DISCOVERY_PORT); |
1542 | 150 | } | 384 | } |
1543 | 151 | 385 | ||
1544 | @@ -153,20 +387,25 @@ | |||
1545 | 153 | if (needupdate) { | 387 | if (needupdate) { |
1546 | 154 | needupdate = false; | 388 | needupdate = false; |
1547 | 155 | 389 | ||
1549 | 156 | broadcast(&gameinfo, sizeof(gameinfo), WIDELANDS_LAN_DISCOVERY_PORT); | 390 | if (!broadcast(&gameinfo, sizeof(gameinfo), WIDELANDS_LAN_DISCOVERY_PORT)) { |
1550 | 391 | report_network_error(); | ||
1551 | 392 | } | ||
1552 | 157 | } | 393 | } |
1553 | 158 | 394 | ||
1555 | 159 | while (avail()) { | 395 | while (is_available()) { |
1556 | 160 | char magic[8]; | 396 | char magic[8]; |
1558 | 161 | sockaddr_in addr; | 397 | NetAddress addr; |
1559 | 162 | 398 | ||
1560 | 163 | if (receive(magic, 8, &addr) < 8) | 399 | if (receive(magic, 8, &addr) < 8) |
1561 | 164 | continue; | 400 | continue; |
1562 | 165 | 401 | ||
1564 | 166 | log("Received %s packet\n", magic); | 402 | log("Received %s packet from %s\n", magic, addr.ip.to_string().c_str()); |
1565 | 167 | 403 | ||
1568 | 168 | if (!strncmp(magic, "QUERY", 6) && magic[6] == LAN_PROMOTION_PROTOCOL_VERSION) | 404 | if (!strncmp(magic, "QUERY", 6) && magic[6] == LAN_PROMOTION_PROTOCOL_VERSION) { |
1569 | 169 | send(&gameinfo, sizeof(gameinfo), &addr); | 405 | if (!send(&gameinfo, sizeof(gameinfo), addr)) { |
1570 | 406 | report_network_error(); | ||
1571 | 407 | } | ||
1572 | 408 | } | ||
1573 | 170 | } | 409 | } |
1574 | 171 | } | 410 | } |
1575 | 172 | 411 | ||
1576 | @@ -178,8 +417,8 @@ | |||
1577 | 178 | 417 | ||
1578 | 179 | /*** class LanGameFinder ***/ | 418 | /*** class LanGameFinder ***/ |
1579 | 180 | 419 | ||
1582 | 181 | LanGameFinder::LanGameFinder() : callback(nullptr) { | 420 | LanGameFinder::LanGameFinder() |
1583 | 182 | bind(WIDELANDS_LAN_DISCOVERY_PORT); | 421 | : LanBase(WIDELANDS_LAN_DISCOVERY_PORT), callback(nullptr) { |
1584 | 183 | 422 | ||
1585 | 184 | reset(); | 423 | reset(); |
1586 | 185 | } | 424 | } |
1587 | @@ -192,18 +431,19 @@ | |||
1588 | 192 | strncpy(magic, "QUERY", 8); | 431 | strncpy(magic, "QUERY", 8); |
1589 | 193 | magic[6] = LAN_PROMOTION_PROTOCOL_VERSION; | 432 | magic[6] = LAN_PROMOTION_PROTOCOL_VERSION; |
1590 | 194 | 433 | ||
1592 | 195 | broadcast(magic, 8, WIDELANDS_LAN_PROMOTION_PORT); | 434 | if (!broadcast(magic, 8, WIDELANDS_LAN_PROMOTION_PORT)) |
1593 | 435 | report_network_error(); | ||
1594 | 196 | } | 436 | } |
1595 | 197 | 437 | ||
1596 | 198 | void LanGameFinder::run() { | 438 | void LanGameFinder::run() { |
1598 | 199 | while (avail()) { | 439 | while (is_available()) { |
1599 | 200 | NetGameInfo info; | 440 | NetGameInfo info; |
1601 | 201 | sockaddr_in addr; | 441 | NetAddress addr; |
1602 | 202 | 442 | ||
1603 | 203 | if (receive(&info, sizeof(info), &addr) < static_cast<int32_t>(sizeof(info))) | 443 | if (receive(&info, sizeof(info), &addr) < static_cast<int32_t>(sizeof(info))) |
1604 | 204 | continue; | 444 | continue; |
1605 | 205 | 445 | ||
1607 | 206 | log("Received %s packet\n", info.magic); | 446 | log("Received %s packet from %s\n", info.magic, addr.ip.to_string().c_str()); |
1608 | 207 | 447 | ||
1609 | 208 | if (strncmp(info.magic, "GAME", 6)) | 448 | if (strncmp(info.magic, "GAME", 6)) |
1610 | 209 | continue; | 449 | continue; |
1611 | @@ -217,6 +457,9 @@ | |||
1612 | 217 | for (NetOpenGame* opengame : opengames) { | 457 | for (NetOpenGame* opengame : opengames) { |
1613 | 218 | if (0 == strncmp(opengame->info.hostname, info.hostname, 128)) { | 458 | if (0 == strncmp(opengame->info.hostname, info.hostname, 128)) { |
1614 | 219 | opengame->info = info; | 459 | opengame->info = info; |
1615 | 460 | if (!opengame->address.is_ipv6() && addr.is_ipv6()) { | ||
1616 | 461 | opengame->address.ip = addr.ip; | ||
1617 | 462 | } | ||
1618 | 220 | callback(GameUpdated, opengame, userdata); | 463 | callback(GameUpdated, opengame, userdata); |
1619 | 221 | was_in_list = true; | 464 | was_in_list = true; |
1620 | 222 | break; | 465 | break; |
1621 | @@ -225,10 +468,8 @@ | |||
1622 | 225 | 468 | ||
1623 | 226 | if (!was_in_list) { | 469 | if (!was_in_list) { |
1624 | 227 | opengames.push_back(new NetOpenGame); | 470 | opengames.push_back(new NetOpenGame); |
1629 | 228 | DIAG_OFF("-Wold-style-cast") | 471 | addr.port = WIDELANDS_PORT; |
1630 | 229 | opengames.back()->address = addr.sin_addr.s_addr; | 472 | opengames.back()->address = addr; |
1627 | 230 | opengames.back()->port = htons(WIDELANDS_PORT); | ||
1628 | 231 | DIAG_ON("-Wold-style-cast") | ||
1631 | 232 | opengames.back()->info = info; | 473 | opengames.back()->info = info; |
1632 | 233 | callback(GameOpened, opengames.back(), userdata); | 474 | callback(GameOpened, opengames.back(), userdata); |
1633 | 234 | break; | 475 | break; |
1634 | 235 | 476 | ||
1635 | === modified file 'src/network/network_lan_promotion.h' | |||
1636 | --- src/network/network_lan_promotion.h 2017-05-07 20:27:21 +0000 | |||
1637 | +++ src/network/network_lan_promotion.h 2017-06-07 18:50:40 +0000 | |||
1638 | @@ -21,21 +21,15 @@ | |||
1639 | 21 | #define WL_NETWORK_NETWORK_LAN_PROMOTION_H | 21 | #define WL_NETWORK_NETWORK_LAN_PROMOTION_H |
1640 | 22 | 22 | ||
1641 | 23 | #include <list> | 23 | #include <list> |
1649 | 24 | 24 | #include <set> | |
1650 | 25 | #ifndef _WIN32 | 25 | |
1651 | 26 | #include <sys/socket.h> | 26 | #include "network/network.h" |
1645 | 27 | #endif | ||
1646 | 28 | #include <sys/types.h> | ||
1647 | 29 | |||
1648 | 30 | #include "network/network_system.h" | ||
1652 | 31 | 27 | ||
1653 | 32 | #define LAN_PROMOTION_PROTOCOL_VERSION 1 | 28 | #define LAN_PROMOTION_PROTOCOL_VERSION 1 |
1654 | 33 | 29 | ||
1655 | 34 | #define LAN_GAME_CLOSED 0 | 30 | #define LAN_GAME_CLOSED 0 |
1656 | 35 | #define LAN_GAME_OPEN 1 | 31 | #define LAN_GAME_OPEN 1 |
1657 | 36 | 32 | ||
1658 | 37 | // TODO(Notabilis): Update file for IPv6 | ||
1659 | 38 | |||
1660 | 39 | struct NetGameInfo { | 33 | struct NetGameInfo { |
1661 | 40 | char magic[6]; | 34 | char magic[6]; |
1662 | 41 | uint8_t version; | 35 | uint8_t version; |
1663 | @@ -47,31 +41,112 @@ | |||
1664 | 47 | }; | 41 | }; |
1665 | 48 | 42 | ||
1666 | 49 | struct NetOpenGame { | 43 | struct NetOpenGame { |
1669 | 50 | in_addr_t address; | 44 | NetAddress address; |
1668 | 51 | in_port_t port; | ||
1670 | 52 | NetGameInfo info; | 45 | NetGameInfo info; |
1671 | 53 | }; | 46 | }; |
1672 | 54 | 47 | ||
1673 | 48 | /** | ||
1674 | 49 | * Base class for UDP networking. | ||
1675 | 50 | * This class is used by derived classes to find open games on the | ||
1676 | 51 | * local network and to announce a just opened game on the local network. | ||
1677 | 52 | * This class tries to create sockets for IPv4 and IPv6. | ||
1678 | 53 | */ | ||
1679 | 55 | struct LanBase { | 54 | struct LanBase { |
1680 | 56 | protected: | 55 | protected: |
1682 | 57 | LanBase(); | 56 | /** |
1683 | 57 | * Tries to start a socket on the given port. | ||
1684 | 58 | * Sockets for IPv4 and IPv6 are started. | ||
1685 | 59 | * When both fail, report_network_error() is called. | ||
1686 | 60 | * \param port The port to listen on. | ||
1687 | 61 | */ | ||
1688 | 62 | LanBase(uint16_t port); | ||
1689 | 63 | |||
1690 | 58 | ~LanBase(); | 64 | ~LanBase(); |
1691 | 59 | 65 | ||
1700 | 60 | void bind(uint16_t); | 66 | /** |
1701 | 61 | 67 | * Returns whether data is available to be read. | |
1702 | 62 | bool avail(); | 68 | * \return \c True when receive() will return data, \c false otherwise. |
1703 | 63 | 69 | */ | |
1704 | 64 | ssize_t receive(void*, size_t, sockaddr_in*); | 70 | bool is_available(); |
1705 | 65 | 71 | ||
1706 | 66 | void send(void const*, size_t, sockaddr_in const*); | 72 | /** |
1707 | 67 | void broadcast(void const*, size_t, uint16_t); | 73 | * Returns whether at least one of the sockets is open. |
1708 | 74 | * If this returns \c false, you probably have a problem. | ||
1709 | 75 | * \return \c True when a socket is ready, \c false otherwise. | ||
1710 | 76 | */ | ||
1711 | 77 | bool is_open(); | ||
1712 | 78 | |||
1713 | 79 | /** | ||
1714 | 80 | * Tries to receive some data. | ||
1715 | 81 | * \param[out] buf The buffer to read data into. | ||
1716 | 82 | * \param len The length of the buffer. | ||
1717 | 83 | * \param[out] addr The address we received data from. Since UDP is a connection-less | ||
1718 | 84 | * protocol, each receive() might receive data from another address. | ||
1719 | 85 | * \return How many bytes have been written to \c buf. If 0 is returned there either was no data | ||
1720 | 86 | * available (check before with avail()) or there was some error (check with is_open()) | ||
1721 | 87 | */ | ||
1722 | 88 | ssize_t receive(void *buf, size_t len, NetAddress *addr); | ||
1723 | 89 | |||
1724 | 90 | /** | ||
1725 | 91 | * Sends data to a specified address. | ||
1726 | 92 | * \param buf The data to send. | ||
1727 | 93 | * \param len The length of the buffer. | ||
1728 | 94 | * \param addr The address to send to. | ||
1729 | 95 | */ | ||
1730 | 96 | bool send(void const *buf, size_t len, const NetAddress& addr); | ||
1731 | 97 | |||
1732 | 98 | /** | ||
1733 | 99 | * Broadcast some data in the local network. | ||
1734 | 100 | * \param buf The data to send. | ||
1735 | 101 | * \param len The length of the buffer. | ||
1736 | 102 | * \param port The port to send to. No address is required. | ||
1737 | 103 | */ | ||
1738 | 104 | bool broadcast(void const* buf, size_t len, uint16_t port); | ||
1739 | 105 | |||
1740 | 106 | /** | ||
1741 | 107 | * Throws a WLWarning exception to jump back to the main menu. | ||
1742 | 108 | * Calling this on network errors is in the responsibility of derived classes. | ||
1743 | 109 | * (Most of the time, aborting makes sense when an error occurred. But e.g. in | ||
1744 | 110 | * the destructor simply ignoring the error is okay.) | ||
1745 | 111 | */ | ||
1746 | 112 | void report_network_error(); | ||
1747 | 68 | 113 | ||
1748 | 69 | private: | 114 | private: |
1752 | 70 | int32_t sock; | 115 | |
1753 | 71 | 116 | /** | |
1754 | 72 | std::list<in_addr_t> broadcast_addresses; | 117 | * Opens a listening UDP socket. |
1755 | 118 | * \param[out] The socket to open. The object has to be created but the socket not opened before. | ||
1756 | 119 | * If it already has been opened before, nothing will be done. | ||
1757 | 120 | * \param version Whether a IPv4 or IPv6 socket should be opened. | ||
1758 | 121 | * \param port The port to listen on. | ||
1759 | 122 | */ | ||
1760 | 123 | void start_socket(boost::asio::ip::udp::socket *socket, boost::asio::ip::udp version, uint16_t port); | ||
1761 | 124 | |||
1762 | 125 | /** | ||
1763 | 126 | * Closes the given socket. | ||
1764 | 127 | * Does nothing if the socket already has been closed. | ||
1765 | 128 | * \param socket The socket to close. | ||
1766 | 129 | */ | ||
1767 | 130 | void close_socket(boost::asio::ip::udp::socket *socket); | ||
1768 | 131 | |||
1769 | 132 | /// No idea what this does. I think it is only really used when asynchronous operations are done. | ||
1770 | 133 | boost::asio::io_service io_service; | ||
1771 | 134 | /// The socket for IPv4. | ||
1772 | 135 | boost::asio::ip::udp::socket socket_v4; | ||
1773 | 136 | /// The socket for IPv6. | ||
1774 | 137 | boost::asio::ip::udp::socket socket_v6; | ||
1775 | 138 | /// The found broadcast addresses for IPv4. | ||
1776 | 139 | /// No addresses for v6, there is only one fixed address. | ||
1777 | 140 | std::set<std::string> broadcast_addresses_v4; | ||
1778 | 141 | #ifdef __APPLE__ | ||
1779 | 142 | /// Apple forces us to define which interface to broadcast through. | ||
1780 | 143 | std::set<unsigned int> interface_indices_v6; | ||
1781 | 144 | #endif // __APPLE__ | ||
1782 | 73 | }; | 145 | }; |
1783 | 74 | 146 | ||
1784 | 147 | /** | ||
1785 | 148 | * Used to promote opened games locally. | ||
1786 | 149 | */ | ||
1787 | 75 | struct LanGamePromoter : public LanBase { | 150 | struct LanGamePromoter : public LanBase { |
1788 | 76 | LanGamePromoter(); | 151 | LanGamePromoter(); |
1789 | 77 | ~LanGamePromoter(); | 152 | ~LanGamePromoter(); |
1790 | @@ -85,6 +160,9 @@ | |||
1791 | 85 | bool needupdate; | 160 | bool needupdate; |
1792 | 86 | }; | 161 | }; |
1793 | 87 | 162 | ||
1794 | 163 | /** | ||
1795 | 164 | * Used to listen for open games while in the LAN-screen. | ||
1796 | 165 | */ | ||
1797 | 88 | struct LanGameFinder : LanBase { | 166 | struct LanGameFinder : LanBase { |
1798 | 89 | enum { GameOpened, GameClosed, GameUpdated }; | 167 | enum { GameOpened, GameClosed, GameUpdated }; |
1799 | 90 | 168 | ||
1800 | 91 | 169 | ||
1801 | === removed file 'src/network/network_system.h' | |||
1802 | --- src/network/network_system.h 2017-01-25 18:55:59 +0000 | |||
1803 | +++ src/network/network_system.h 1970-01-01 00:00:00 +0000 | |||
1804 | @@ -1,59 +0,0 @@ | |||
1805 | 1 | /* | ||
1806 | 2 | * Copyright (C) 2004-2017 by the Widelands Development Team | ||
1807 | 3 | * | ||
1808 | 4 | * This program is free software; you can redistribute it and/or | ||
1809 | 5 | * modify it under the terms of the GNU General Public License | ||
1810 | 6 | * as published by the Free Software Foundation; either version 2 | ||
1811 | 7 | * of the License, or (at your option) any later version. | ||
1812 | 8 | * | ||
1813 | 9 | * This program is distributed in the hope that it will be useful, | ||
1814 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1815 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1816 | 12 | * GNU General Public License for more details. | ||
1817 | 13 | * | ||
1818 | 14 | * You should have received a copy of the GNU General Public License | ||
1819 | 15 | * along with this program; if not, write to the Free Software | ||
1820 | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
1821 | 17 | * | ||
1822 | 18 | */ | ||
1823 | 19 | |||
1824 | 20 | #ifndef WL_NETWORK_NETWORK_SYSTEM_H | ||
1825 | 21 | #define WL_NETWORK_NETWORK_SYSTEM_H | ||
1826 | 22 | |||
1827 | 23 | #include <stdint.h> | ||
1828 | 24 | #ifndef _WIN32 | ||
1829 | 25 | // These includes work on Linux and should be fine on any other Unix-alike. | ||
1830 | 26 | // If not, this is the right place to conditionally include what is needed. | ||
1831 | 27 | #include <net/if.h> | ||
1832 | 28 | #include <netdb.h> | ||
1833 | 29 | #include <netinet/in.h> | ||
1834 | 30 | #include <sys/ioctl.h> | ||
1835 | 31 | #include <sys/socket.h> | ||
1836 | 32 | #include <sys/types.h> | ||
1837 | 33 | #include <unistd.h> | ||
1838 | 34 | |||
1839 | 35 | // be compatible to microsoft | ||
1840 | 36 | #define closesocket close | ||
1841 | 37 | #define DATATYPE void | ||
1842 | 38 | |||
1843 | 39 | #else | ||
1844 | 40 | |||
1845 | 41 | // This is the header to include according to the documentation | ||
1846 | 42 | // at msdn.microsoft.com | ||
1847 | 43 | #include <winsock2.h> | ||
1848 | 44 | |||
1849 | 45 | #define DATATYPE char | ||
1850 | 46 | // microsoft doesn't have these | ||
1851 | 47 | using in_port_t = uint16_t; | ||
1852 | 48 | using in_addr_t = uint32_t; | ||
1853 | 49 | |||
1854 | 50 | #ifndef s_addr | ||
1855 | 51 | #define s_addr S_addr | ||
1856 | 52 | #endif | ||
1857 | 53 | |||
1858 | 54 | // This is no typedef on purpose | ||
1859 | 55 | #define socklen_t int32_t | ||
1860 | 56 | |||
1861 | 57 | #endif | ||
1862 | 58 | |||
1863 | 59 | #endif // end of include guard: WL_NETWORK_NETWORK_SYSTEM_H | ||
1864 | 60 | 0 | ||
1865 | === modified file 'src/ui_fsmenu/internet_lobby.cc' | |||
1866 | --- src/ui_fsmenu/internet_lobby.cc 2017-05-11 06:31:35 +0000 | |||
1867 | +++ src/ui_fsmenu/internet_lobby.cc 2017-06-07 18:50:40 +0000 | |||
1868 | @@ -373,15 +373,10 @@ | |||
1869 | 373 | } | 373 | } |
1870 | 374 | std::string ip = InternetGaming::ref().ip(); | 374 | std::string ip = InternetGaming::ref().ip(); |
1871 | 375 | 375 | ||
1881 | 376 | // TODO(Notabilis): Change this for IPv6 | 376 | NetAddress addr; |
1882 | 377 | // convert IPv6 addresses returned by the metaserver to IPv4 addresses. | 377 | NetAddress::parse_ip(&addr, ip, WIDELANDS_PORT); |
1883 | 378 | // At the moment SDL_net does not support IPv6 anyways. | 378 | assert(addr.is_valid()); |
1884 | 379 | if (!ip.compare(0, 7, "::ffff:")) { | 379 | GameClient netgame(addr, InternetGaming::ref().get_local_clientname(), true); |
1876 | 380 | ip = ip.substr(7); | ||
1877 | 381 | log("InternetGaming: cut IPv6 address: %s\n", ip.c_str()); | ||
1878 | 382 | } | ||
1879 | 383 | |||
1880 | 384 | GameClient netgame(ip, WIDELANDS_PORT, InternetGaming::ref().get_local_clientname(), true); | ||
1885 | 385 | netgame.run(); | 380 | netgame.run(); |
1886 | 386 | } else | 381 | } else |
1887 | 387 | throw wexception("No server selected! That should not happen!"); | 382 | throw wexception("No server selected! That should not happen!"); |
1888 | @@ -404,6 +399,12 @@ | |||
1889 | 404 | InternetGaming::ref().set_local_servername(servername_ui); | 399 | InternetGaming::ref().set_local_servername(servername_ui); |
1890 | 405 | 400 | ||
1891 | 406 | // Start the game | 401 | // Start the game |
1894 | 407 | GameHost netgame(InternetGaming::ref().get_local_clientname(), true); | 402 | try { |
1895 | 408 | netgame.run(); | 403 | GameHost netgame(InternetGaming::ref().get_local_clientname(), true); |
1896 | 404 | netgame.run(); | ||
1897 | 405 | } catch (...) { | ||
1898 | 406 | // Log out before going back to the main menu | ||
1899 | 407 | InternetGaming::ref().logout("SERVER_CRASHED"); | ||
1900 | 408 | throw; | ||
1901 | 409 | } | ||
1902 | 409 | } | 410 | } |
1903 | 410 | 411 | ||
1904 | === modified file 'src/ui_fsmenu/netsetup_lan.cc' | |||
1905 | --- src/ui_fsmenu/netsetup_lan.cc 2017-02-27 13:45:46 +0000 | |||
1906 | +++ src/ui_fsmenu/netsetup_lan.cc 2017-06-07 18:50:40 +0000 | |||
1907 | @@ -135,7 +135,7 @@ | |||
1908 | 135 | discovery.run(); | 135 | discovery.run(); |
1909 | 136 | } | 136 | } |
1910 | 137 | 137 | ||
1912 | 138 | bool FullscreenMenuNetSetupLAN::get_host_address(uint32_t& addr, uint16_t& port) { | 138 | bool FullscreenMenuNetSetupLAN::get_host_address(NetAddress *addr) { |
1913 | 139 | const std::string& host = hostname.text(); | 139 | const std::string& host = hostname.text(); |
1914 | 140 | 140 | ||
1915 | 141 | const uint32_t opengames_size = opengames.size(); | 141 | const uint32_t opengames_size = opengames.size(); |
1916 | @@ -143,20 +143,17 @@ | |||
1917 | 143 | const NetOpenGame& game = *opengames[i]; | 143 | const NetOpenGame& game = *opengames[i]; |
1918 | 144 | 144 | ||
1919 | 145 | if (!strcmp(game.info.hostname, host.c_str())) { | 145 | if (!strcmp(game.info.hostname, host.c_str())) { |
1922 | 146 | addr = game.address; | 146 | *addr = game.address; |
1921 | 147 | port = game.port; | ||
1923 | 148 | return true; | 147 | return true; |
1924 | 149 | } | 148 | } |
1925 | 150 | } | 149 | } |
1926 | 151 | 150 | ||
1935 | 152 | if (hostent* const he = gethostbyname(host.c_str())) { | 151 | // The user probably entered a hostname on his own. Try to resolve it |
1936 | 153 | addr = (reinterpret_cast<in_addr*>(he->h_addr_list[0]))->s_addr; | 152 | if (NetAddress::resolve_to_v6(addr, host, WIDELANDS_PORT)) |
1937 | 154 | DIAG_OFF("-Wold-style-cast") | 153 | return true; |
1938 | 155 | port = htons(WIDELANDS_PORT); | 154 | if (NetAddress::resolve_to_v4(addr, host, WIDELANDS_PORT)) |
1939 | 156 | DIAG_ON("-Wold-style-cast") | 155 | return true; |
1940 | 157 | return true; | 156 | return false; |
1933 | 158 | } else | ||
1934 | 159 | return false; | ||
1941 | 160 | } | 157 | } |
1942 | 161 | 158 | ||
1943 | 162 | const std::string& FullscreenMenuNetSetupLAN::get_playername() { | 159 | const std::string& FullscreenMenuNetSetupLAN::get_playername() { |
1944 | 163 | 160 | ||
1945 | === modified file 'src/ui_fsmenu/netsetup_lan.h' | |||
1946 | --- src/ui_fsmenu/netsetup_lan.h 2017-05-07 20:27:21 +0000 | |||
1947 | +++ src/ui_fsmenu/netsetup_lan.h 2017-06-07 18:50:40 +0000 | |||
1948 | @@ -34,8 +34,6 @@ | |||
1949 | 34 | struct NetOpenGame; | 34 | struct NetOpenGame; |
1950 | 35 | struct NetGameInfo; | 35 | struct NetGameInfo; |
1951 | 36 | 36 | ||
1952 | 37 | // TODO(Notabilis): Update for IPv6 | ||
1953 | 38 | |||
1954 | 39 | class FullscreenMenuNetSetupLAN : public FullscreenMenuBase { | 37 | class FullscreenMenuNetSetupLAN : public FullscreenMenuBase { |
1955 | 40 | public: | 38 | public: |
1956 | 41 | FullscreenMenuNetSetupLAN(); | 39 | FullscreenMenuNetSetupLAN(); |
1957 | @@ -43,12 +41,10 @@ | |||
1958 | 43 | void think() override; | 41 | void think() override; |
1959 | 44 | 42 | ||
1960 | 45 | /** | 43 | /** |
1965 | 46 | * \param[out] addr filled in with the IP address of the chosen server | 44 | * \param[out] addr filled in with the host name or IP address and port of the chosen server |
1966 | 47 | * \param[out] port filled in with the port of the chosen server | 45 | * \return \c True if the address is valid, \c false otherwise. In that case \c addr is not modified |
1963 | 48 | * \return \c true if a valid server has been chosen. If \c false is | ||
1964 | 49 | * returned, the values of \p addr and \p port are undefined. | ||
1967 | 50 | */ | 46 | */ |
1969 | 51 | bool get_host_address(uint32_t& addr, uint16_t& port); | 47 | bool get_host_address(NetAddress *addr); |
1970 | 52 | 48 | ||
1971 | 53 | /** | 49 | /** |
1972 | 54 | * \return the name chosen by the player | 50 | * \return the name chosen by the player |
1973 | 55 | 51 | ||
1974 | === modified file 'src/wlapplication.cc' | |||
1975 | --- src/wlapplication.cc 2017-05-13 13:14:29 +0000 | |||
1976 | +++ src/wlapplication.cc 2017-06-07 18:50:40 +0000 | |||
1977 | @@ -350,9 +350,6 @@ | |||
1978 | 350 | // This might grab the input. | 350 | // This might grab the input. |
1979 | 351 | refresh_graphics(); | 351 | refresh_graphics(); |
1980 | 352 | 352 | ||
1981 | 353 | if (SDLNet_Init() == -1) | ||
1982 | 354 | throw wexception("SDLNet_Init failed: %s\n", SDLNet_GetError()); | ||
1983 | 355 | |||
1984 | 356 | // seed random number generator used for random tribe selection | 353 | // seed random number generator used for random tribe selection |
1985 | 357 | std::srand(time(nullptr)); | 354 | std::srand(time(nullptr)); |
1986 | 358 | 355 | ||
1987 | @@ -378,8 +375,6 @@ | |||
1988 | 378 | delete UI::g_fh1; | 375 | delete UI::g_fh1; |
1989 | 379 | UI::g_fh1 = nullptr; | 376 | UI::g_fh1 = nullptr; |
1990 | 380 | 377 | ||
1991 | 381 | SDLNet_Quit(); | ||
1992 | 382 | |||
1993 | 383 | TTF_Quit(); // TODO(unknown): not here | 378 | TTF_Quit(); // TODO(unknown): not here |
1994 | 384 | 379 | ||
1995 | 385 | assert(g_fs); | 380 | assert(g_fs); |
1996 | @@ -1164,10 +1159,6 @@ | |||
1997 | 1164 | FullscreenMenuNetSetupLAN ns; | 1159 | FullscreenMenuNetSetupLAN ns; |
1998 | 1165 | menu_result = ns.run<FullscreenMenuBase::MenuTarget>(); | 1160 | menu_result = ns.run<FullscreenMenuBase::MenuTarget>(); |
1999 | 1166 | std::string playername = ns.get_playername(); | 1161 | std::string playername = ns.get_playername(); |
2000 | 1167 | // TODO(Notabilis): This has to be updated for IPv6 | ||
2001 | 1168 | uint32_t addr; | ||
2002 | 1169 | uint16_t port; | ||
2003 | 1170 | bool const host_address = ns.get_host_address(addr, port); | ||
2004 | 1171 | 1162 | ||
2005 | 1172 | switch (menu_result) { | 1163 | switch (menu_result) { |
2006 | 1173 | case FullscreenMenuBase::MenuTarget::kHostgame: { | 1164 | case FullscreenMenuBase::MenuTarget::kHostgame: { |
2007 | @@ -1176,18 +1167,16 @@ | |||
2008 | 1176 | break; | 1167 | break; |
2009 | 1177 | } | 1168 | } |
2010 | 1178 | case FullscreenMenuBase::MenuTarget::kJoingame: { | 1169 | case FullscreenMenuBase::MenuTarget::kJoingame: { |
2014 | 1179 | if (!host_address) | 1170 | NetAddress addr; |
2015 | 1180 | throw WLWarning( | 1171 | if (!ns.get_host_address(&addr)) { |
2016 | 1181 | "Invalid Address", "%s", "The address of the game server is invalid"); | 1172 | UI::WLMessageBox mmb(&ns, _("Invalid address"), |
2017 | 1173 | _("The entered hostname or address is invalid and can’t be connected to."), | ||
2018 | 1174 | UI::WLMessageBox::MBoxType::kOk); | ||
2019 | 1175 | mmb.run<UI::Panel::Returncodes>(); | ||
2020 | 1176 | break; | ||
2021 | 1177 | } | ||
2022 | 1182 | 1178 | ||
2031 | 1183 | // TODO(Notabilis): Make this prettier. I am aware that this is quite ugly but it should | 1179 | GameClient netgame(addr, playername); |
2024 | 1184 | // work | ||
2025 | 1185 | // for now and will be removed shortly when we switch to boost.asio | ||
2026 | 1186 | char ip_str[] = {"255.255.255.255"}; | ||
2027 | 1187 | sprintf(ip_str, "%d.%d.%d.%d", (addr & 0x000000ff), (addr & 0x0000ff00) >> 8, | ||
2028 | 1188 | (addr & 0x00ff0000) >> 16, (addr & 0xff000000) >> 24); | ||
2029 | 1189 | port = (port >> 8) | ((port & 0xFF) << 8); | ||
2030 | 1190 | GameClient netgame(ip_str, port, playername); | ||
2032 | 1191 | netgame.run(); | 1180 | netgame.run(); |
2033 | 1192 | break; | 1181 | break; |
2034 | 1193 | } | 1182 | } |
2035 | 1194 | 1183 | ||
2036 | === modified file 'utils/macos/build_app.sh' | |||
2037 | --- utils/macos/build_app.sh 2016-11-30 00:18:17 +0000 | |||
2038 | +++ utils/macos/build_app.sh 2017-06-07 18:50:40 +0000 | |||
2039 | @@ -109,7 +109,6 @@ | |||
2040 | 109 | export SDL2IMAGEDIR="$(brew --prefix sdl2_image)" | 109 | export SDL2IMAGEDIR="$(brew --prefix sdl2_image)" |
2041 | 110 | export SDL2MIXERDIR="$(brew --prefix sdl2_mixer)" | 110 | export SDL2MIXERDIR="$(brew --prefix sdl2_mixer)" |
2042 | 111 | export SDL2TTFDIR="$(brew --prefix sdl2_ttf)" | 111 | export SDL2TTFDIR="$(brew --prefix sdl2_ttf)" |
2043 | 112 | export SDL2NETDIR="$(brew --prefix sdl2_net)" | ||
2044 | 113 | export BOOST_ROOT="$(brew --prefix boost)" | 112 | export BOOST_ROOT="$(brew --prefix boost)" |
2045 | 114 | export ICU_ROOT="$(brew --prefix icu4c)" | 113 | export ICU_ROOT="$(brew --prefix icu4c)" |
2046 | 115 | 114 | ||
2047 | 116 | 115 | ||
2048 | === modified file 'utils/win32/innosetup/Widelands.iss' | |||
2049 | --- utils/win32/innosetup/Widelands.iss 2017-02-27 08:52:41 +0000 | |||
2050 | +++ utils/win32/innosetup/Widelands.iss 2017-06-07 18:50:40 +0000 | |||
2051 | @@ -131,7 +131,6 @@ | |||
2052 | 131 | Source: {#DLLFolder}\SDL2.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" | 131 | Source: {#DLLFolder}\SDL2.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" |
2053 | 132 | Source: {#DLLFolder}\SDL2_image.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" | 132 | Source: {#DLLFolder}\SDL2_image.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" |
2054 | 133 | Source: {#DLLFolder}\libSDL2_mixer-2-0-0.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" | 133 | Source: {#DLLFolder}\libSDL2_mixer-2-0-0.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" |
2055 | 134 | Source: {#DLLFolder}\SDL2_net.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" | ||
2056 | 135 | Source: {#DLLFolder}\SDL2_ttf.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" | 134 | Source: {#DLLFolder}\SDL2_ttf.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" |
2057 | 136 | Source: {#DLLFolder}\zlib1.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" | 135 | Source: {#DLLFolder}\zlib1.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" |
2058 | 137 | Source: {#DLLFolder}\libFLAC-8.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" | 136 | Source: {#DLLFolder}\libFLAC-8.dll; DestDir: {app}; Flags: ignoreversion; Components: "Widelands" |
Continuous integration builds have changed state:
Travis build 2223. State: failed. Details: https:/ /travis- ci.org/ widelands/ widelands/ builds/ 234386018. /ci.appveyor. com/project/ widelands- dev/widelands/ build/_ widelands_ dev_widelands_ net_boost_ asio-2058.
Appveyor build 2058. State: failed. Details: https:/