Skip to content

Commit 019ceed

Browse files
committed
docs/README-migration.md: Written!
Reference Issue #125.
1 parent a5545aa commit 019ceed

1 file changed

Lines changed: 318 additions & 3 deletions

File tree

docs/README-migration.md

Lines changed: 318 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,321 @@
1-
21
# Migrating to SDL_net 3.0
32

4-
This guide provides useful information for migrating applications from SDL_net 2.0 to SDL_net 3.0.
3+
SDL_net 3.0 (aka "SDL3_net") is a dramatically different library than
4+
previous versions. The API has been completely redesigned. There is no
5+
compatibility layer. If you want to use it, you have to migrate to it.
6+
7+
SDL3_net requires SDL3. It relies on many features that are new to SDL3,
8+
both internally and in the public API, so if your project is on SDL 1.2 or
9+
SDL2, you'll have to move your project to SDL3 at the same time.
10+
11+
That being said, we think SDL3_net and SDL3 are both great pieces of
12+
software--significant improvements over their previous versions--and we
13+
think that once you move to them, you'll be quite happy you did.
14+
15+
There are some things that don't have simple replacements that can be changed
16+
mechanically to migrate to SDL3_net. The new API is in many ways more
17+
powerful, but also much simpler.
18+
19+
This migration guide will attempt to walk through the important details but
20+
it's possible that some things can't be done the same way. Feel free to open
21+
bug reports if you're totally stuck.
22+
23+
The end of this document provides a "tl;dr" section that lists each SDL2_net
24+
function name and a brief explanation about what to do with it.
25+
26+
27+
## Things that are totally gone
28+
29+
- Building SDL_net without SDL at all (the `WITHOUT_SDL` define in SDL2_net)
30+
is no longer an option. SDL3 is required, both in including the SDL_net
31+
public headers and in the library's implementation code.
32+
- Network addresses used to be in a public struct (IPaddress). The new object
33+
(NET_Address) is an opaque type. If your app was setting or reading IPv4
34+
addresses directly in the struct, this will have to change. SDL3_net offers
35+
functions to create and query NET_Address objects.
36+
- UDP transmission no longer deals with "channels." This was a confusing
37+
interface in general. One could build a channel system on top of SDL3_net
38+
easily with an array of NET_Address objects, but NET_DatagramSocket only
39+
deals with sending and receiving from addresses now.
40+
- UDP packets had several API functions for memory management. In SDL3_net,
41+
you just provide a pointer to data you want to send, or a buffer to receive
42+
into, and that's it. Internal memory management is done with SDL_malloc().
43+
- Socket sets are gone. The entire API has collapsed down into
44+
NET_WaitUntilInputAvailable(), which just takes an array of
45+
sockets to check.
46+
47+
48+
## Including SDL3_net
49+
50+
The proper way to include SDL3_net's header is:
51+
52+
```c
53+
#include <SDL3_net/SDL_net.h>
54+
```
55+
56+
Like SDL3, the new convention is to use `<>` brackets and a subdirectory.
57+
58+
59+
## Symbol names
60+
61+
In SDL2_net, all functions started with `SDLNet_` and macros started with
62+
`SDLNET_`. In SDL3_net, everything starts with `NET_`.
63+
64+
65+
## Versioning
66+
67+
Versions are now bits packed into a single int instead of a struct, using
68+
SDL3's usual magic for unpacking these.
69+
70+
71+
## Return values
72+
73+
Most things that returned an int in SDL2_net would return a -1 or 0 to report
74+
error or success, respectively. In SDL3_net, these sorts of functions return
75+
bools, with true for success and false for failure. Be careful in migrating,
76+
as your compiler might catch this and warn you...
77+
78+
```c
79+
if (NET_Init() < 0) { /* failure! */ }
80+
```
81+
82+
...but it won't catch this...
83+
84+
```c
85+
if (!NET_Init()) { /* Success in SDL2_net, but failure in SDL3_net! */ }
86+
```
87+
88+
89+
## Multiple protocols
90+
91+
SDL2_net's public API was hardcoded to offer IPv4 networking. SDL3_net has
92+
replaced its IPaddress struct with the opaque NET_Address. This can handle
93+
addresses from different network protocols, including future ones that haven't
94+
been conceived of yet. But most notably: it means that your app can abstractly
95+
handle both IPv4 and IPv6.
96+
97+
(In theory, this could also work with, say, an IPX or AppleTalk network in the
98+
wild, but it might require small fixes to SDL3_net's code. Send patches.)
99+
100+
Using NET_ResolveHostname() might give you any available protocol, letting
101+
the OS decide the best option for the system.
102+
103+
SDL3_net goes even further: it offers "dual-stack" sockets. If you create a
104+
server or datagram socket bound to a NULL address, it will try to create both
105+
IPv4 and IPv6 OS-level sockets under the hood, and listen on both. This is
106+
not just an IPv4-over-IPv6 bridge. This makes it possible that you'll get
107+
traffic from both protocols at the same time, with clients coming from
108+
addresses that look like 157.90.7.176 and 2a01:4f8:251:5583::2 simultaneously,
109+
and your server can trivially bridge between them.
110+
111+
112+
## Blocking vs Non-blocking
113+
114+
SDL2_net was (mostly) a blocking API. If it took time to complete a network
115+
operation, you would have to wait. Some waits might be short, like passing a
116+
datagram to the kernel's transmit buffers, but others might take many seconds,
117+
depending on network conditions. It offered a wrapper over the select() call
118+
with what it called "socket sets" to query if data was available, to avoid
119+
blocking. A DNS lookup was going to take as long as it takes.
120+
121+
SDL3_net takes the opposite approach: everything is _non-blocking_ now. If
122+
data is not available, read calls will return immediately, reporting this.
123+
If data cannot be sent right away, it will queue internally until the library
124+
can send it later. DNS queries happen on a pool of background threads and the
125+
app can check in from time to time to see if there are results available.
126+
127+
The concept of "socket sets" has collapsed down into a single function:
128+
NET_WaitUntilInputAvailable(), which takes an array of various SDL_net
129+
objects and a timeout. A non-blocking query has a timeout of zero, an
130+
indefinite wait until _something_ happens is a timeout of -1.
131+
132+
If you prefer blocking behavior, there are functions to wait for specific
133+
things to happen (with an optional timeout). These will put the calling
134+
thread to sleep for efficiency.
135+
136+
- NET_WaitUntilResolved: Wait for a specific DNS query to finish.
137+
- NET_WaitUntilConnected: Wait for a specific stream socket to connect.
138+
- NET_WaitUntilStreamSocketDrained: Wait for a stream socket to send all
139+
queued data.
140+
- NET_WaitUntilInputAvailable: Like the other NET_WaitUntil* functions, but
141+
check multiple things at the same time.
142+
143+
There are simple non-blocking query functions, too: NET_GetAddressStatus,
144+
NET_GetConnectionStatus, NET_GetStreamSocketPendingWrites.
145+
146+
147+
## Thread safety
148+
149+
All functions in SDL3_net are thread safe, with one caveat: you can not
150+
operate on the same socket from two threads at once. Two threads can work
151+
with two separate sockets at the same time without problems.
152+
153+
154+
## Network byte order
155+
156+
In SDL2_net, things like network addresses and port numbers might need to be
157+
in "network byte order" (bigendian) in some cases. In SDL3_net, these values
158+
are always in "host byte order" (the native byte order of the system), and
159+
SDL3_net will manage byteswapping behind the scenes as necessary.
160+
161+
Of course any payload bytes sent over the internet might comes from any
162+
computer with any byte order, so the app still needs to deal with that exactly
163+
the same as they did with SDL2_net.
164+
165+
166+
## Initialization
167+
168+
SDL2_net expected you to call SDL_Init() before SDLNet_Init(). Now you don't
169+
have to.
170+
171+
```c
172+
if (!NET_Init()) {
173+
SDL_Log("NET_Init failed: %s", SDL_GetError());
174+
} else {
175+
SDL_Log("SDL_net is ready!");
176+
}
177+
```
178+
179+
NET_Init() is reference-counted; it's safe to call it more than once, and
180+
only the first call will do actual initialization tasks. Likewise, actual
181+
deinitialization with NET_Quit() will only happen when it has been called
182+
as many times as NET_Init() has. This allows different parts of the program
183+
to initialize and use SDL3_net without knowing about other parts using it
184+
too.
185+
186+
187+
## Stream sockets
188+
189+
SDL2_net represents server ("listen") sockets with the same type as connected
190+
sockets: a TCPsocket object. In SDL3_net, these are split into two separate
191+
objects: NET_Server and NET_StreamSocket.
192+
193+
NET_StreamSocket is the thing that actually transmits data over the network
194+
between a client and server; both ends of the connection have one and both
195+
can read or write to theirs.
196+
197+
NET_Server is the thing that will listen for new connections from clients, and
198+
generate the server's end of the NET_StreamSocket for each one.
199+
200+
Creating NET_Server with a NULL address will possibly listen on both IPv4 and
201+
IPv6 networks separately under the hood.
202+
203+
204+
## Datagram sockets
205+
206+
SDLNet_UDP_Open() would let you specify a port but only binds to INADDR_ANY.
207+
SDL3_net's NET_CreateDatagramSocket() will let you bind to specific interfaces
208+
or specify a NULL address for the equivalent of INADDR_ANY (but it might
209+
listen on both IPv4 and IPv6 interfaces under the hood).
210+
211+
212+
## Broadcasting
213+
214+
In SDL2_net, you could send UDP packets to the subnet's broadcast address
215+
(if you could figure it out), or 255.255.255.255 (which wouldn't work on
216+
Windows).
217+
218+
In SDL3_net, you create a datagram socket with the
219+
`NET_PROP_DATAGRAM_SOCKET_ALLOW_BROADCAST_BOOLEAN` property, and call
220+
NET_SendDatagram() with a NULL address.
221+
222+
This abstracts out broadcasting for different protocols (including IPv6,
223+
where SDL3_net fakes this under the hood with multicasting on your behalf).
224+
SDL3_net will also make efforts to broadcast to all network interfaces if
225+
appropriate, and broadcast to different network protocols. As such, it's
226+
possible that a single broadcast packet will arrive for the same app on
227+
multiple interfaces, and it might appear to come from different computers,
228+
since it might have an IPv4 and IPv6 address. Plan accordingly, so you know
229+
whether to drop duplicate packets.
230+
231+
Futher, if you broadcast from a client in order to locate servers, and that
232+
server is listening on both IPv4 and IPv6, you might get two responses that
233+
appear to be different servers. Plan to add something unique in the payload
234+
so you can recognize they are the same machine, or separate out IPv4/IPv6
235+
servers in your server browser, etc.
236+
237+
(And, of course, please limit the amount of broadcasting your app does in
238+
general, to be a good citizen of your subnet!)
239+
240+
241+
## Simulating failure
242+
243+
SDL2_net had SDLNet_UDP_SetPacketLoss(). SDL3_net extends this to other pieces
244+
of the network stack: you can set up separate simulated failure percentages
245+
for stream sockets (being reliable, they will introduce delays into the stream
246+
and possibly just drop the connection outright sometimes), and DNS lookups
247+
(some will take longer, and some will fail, as if packets aren't arriving from
248+
the DNS server or domains are mysteriously missing).
249+
250+
These functions are NET_SimulateAddressResolutionLoss(),
251+
NET_SimulateStreamPacketLoss(), and NET_SimulateDatagramPacketLoss().
252+
253+
254+
## tl;dr
255+
256+
A very brief comment on what to do with each symbol in SDL2_net.
257+
258+
Some of these are listed as "no equivalent in SDL3_net" but could possibly
259+
be added if there is a need. If you're stuck, please file an issue and we
260+
can discuss it!
261+
262+
- SDL_NET_MAJOR_VERSION => SDL_NET_MAJOR_VERSION
263+
- SDL_NET_MINOR_VERSION => SDL_NET_MAJOR_VERSION
264+
- SDL_NET_PATCHLEVEL => SDL_NET_MICRO_VERSION
265+
- SDL_NET_VERSION => SDL_NET_VERSION
266+
- SDL_NET_VERSION_ATLEAST => SDL_NET_VERSION_ATLEAST
267+
- SDLNet_Linked_Version => NET_Version
268+
- SDL_NET_COMPILEDVERSION => SDL_NET_VERSION
269+
- SDLNet_Init => NET_Init
270+
- SDLNet_Quit => NET_Quit
271+
- IPaddress => NET_Address
272+
- SDLNet_ResolveHost => NET_ResolveHostname (the port number from SDL2_net is specified in other functions).
273+
- SDLNet_ResolveIP => NET_GetAddressString (this does not attempt to figure out the hostname via DNS, though! File an issue if you need this!)
274+
- SDLNet_GetLocalAddresses => NET_GetLocalAddresses
275+
- TCPsocket => NET_StreamSocket
276+
- SDLNet_TCP_OpenServer => NET_CreateServer
277+
- SDLNet_TCP_OpenClient => NET_CreateClient
278+
- SDLNet_TCP_Open => No direct equivalent, use NET_CreateServer or NET_CreateClient as appropriate.
279+
- SDLNet_TCP_Accept => NET_AcceptClient
280+
- SDLNet_TCP_GetPeerAddress => NET_GetStreamSocketAddress
281+
- SDLNet_TCP_Send => NET_WriteToStreamSocket
282+
- SDLNet_TCP_Recv => NET_ReadFromStreamSocket
283+
- SDLNet_TCP_Close => NET_DestroyStreamSocket
284+
- SDLNET_MAX_UDPCHANNELS => no equivalent in SDL3_net.
285+
- SDLNET_MAX_UDPADDRESSES => no equivalent in SDL3_net.
286+
- UDPpacket => NET_Datagram
287+
- SDLNet_AllocPacket => no equivalent in SDL3_net.
288+
- SDLNet_ResizePacket => no equivalent in SDL3_net.
289+
- SDLNet_FreePacket => NET_DestroyDatagram
290+
- SDLNet_AllocPacketV => no equivalent in SDL3_net.
291+
- SDLNet_FreePacketV => NET_DestroyDatagram in a loop.
292+
- UDPsocket => NET_DatagramSocket
293+
- SDLNet_UDP_Open => NET_CreateDatagramSocket
294+
- SDLNet_UDP_SetPacketLoss => NET_SimulateDatagramPacketLoss
295+
- SDLNet_UDP_Bind => no equivalent in SDL3_net.
296+
- SDLNet_UDP_Unbind => no equivalent in SDL3_net.
297+
- SDLNet_UDP_GetPeerAddress => no equivalent in SDL3_net. NET_Datagram::addr has it in received packets.
298+
- SDLNet_UDP_SendV => NET_SendDatagram in a loop.
299+
- SDLNet_UDP_Send => NET_SendDatagram
300+
- SDLNet_UDP_RecvV => NET_ReceiveDatagram in a loop.
301+
- SDLNet_UDP_Recv => NET_ReceiveDatagram
302+
- SDLNet_UDP_Close => NET_DestroyDatagramSocket
303+
- SDLNet_GenericSocket => `void *`
304+
- SDLNet_SocketSet => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
305+
- SDLNet_AllocSocketSet => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
306+
- SDLNet_AddSocket => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
307+
- SDLNet_TCP_AddSocket => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
308+
- SDLNet_UDP_AddSocket => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
309+
- SDLNet_DelSocket => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
310+
- SDLNet_TCP_DelSocket => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
311+
- SDLNet_UDP_DelSocket => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
312+
- SDLNet_CheckSockets => NET_WaitUntilInputAvailable
313+
- SDLNet_SocketReady => no equivalent in SDL3_net. Just try to use them, they're all non-blocking.
314+
- SDLNet_FreeSocketSet => no equivalent in SDL3_net. Just pass an array of things to NET_WaitUntilInputAvailable().
315+
- SDLNet_SetError => SDL_SetError
316+
- SDLNet_GetError => SDL_GetError
317+
- SDLNet_Write16 => SDL_SwapBE16
318+
- SDLNet_Write32 => SDL_SwapBE32
319+
- SDLNet_Read16 => SDL_SwapBE16
320+
- SDLNet_Read32 => SDL_SwapBE32
5321

6-
SDLNet_GetError() has been replaced with SDL_GetError().

0 commit comments

Comments
 (0)