From 79304f24b8be6edd740439bf5e1e6748a6ce09bd Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 28 Apr 2013 04:37:14 +1000 Subject: [PATCH] Imported basics project directory client - scene system server - empty main SDL_net source graphical resources --- client/base_scene.cpp | 114 +++++++ client/base_scene.h | 53 +++ client/main.cpp | 20 ++ client/makefile | 19 ++ client/scene_list.h | 14 + client/scene_manager.cpp | 76 +++++ client/scene_manager.h | 27 ++ client/test_systems.cpp | 61 ++++ client/test_systems.h | 27 ++ client/unit.cpp | 0 docs/pseudocode.txt | 24 ++ libs/SDL_net/SDL_net.h | 452 ++++++++++++++++++++++++ libs/SDL_net/SDLnet.c | 269 +++++++++++++++ libs/SDL_net/SDLnetTCP.c | 295 ++++++++++++++++ libs/SDL_net/SDLnetUDP.c | 528 +++++++++++++++++++++++++++++ libs/SDL_net/SDLnetselect.c | 163 +++++++++ libs/SDL_net/SDLnetsys.h | 94 +++++ rsc/graphics/sprites/aniflower.png | Bin 0 -> 869 bytes rsc/graphics/sprites/elliot2.bmp | Bin 0 -> 73782 bytes rsc/graphics/tilesets/terrain.bmp | Bin 0 -> 553014 bytes server/makefile | 19 ++ server/server.cpp | 8 + server/unit.cpp | 0 23 files changed, 2263 insertions(+) create mode 100644 client/base_scene.cpp create mode 100644 client/base_scene.h create mode 100644 client/main.cpp create mode 100644 client/makefile create mode 100644 client/scene_list.h create mode 100644 client/scene_manager.cpp create mode 100644 client/scene_manager.h create mode 100644 client/test_systems.cpp create mode 100644 client/test_systems.h create mode 100644 client/unit.cpp create mode 100644 docs/pseudocode.txt create mode 100644 libs/SDL_net/SDL_net.h create mode 100644 libs/SDL_net/SDLnet.c create mode 100644 libs/SDL_net/SDLnetTCP.c create mode 100644 libs/SDL_net/SDLnetUDP.c create mode 100644 libs/SDL_net/SDLnetselect.c create mode 100644 libs/SDL_net/SDLnetsys.h create mode 100644 rsc/graphics/sprites/aniflower.png create mode 100644 rsc/graphics/sprites/elliot2.bmp create mode 100644 rsc/graphics/tilesets/terrain.bmp create mode 100644 server/makefile create mode 100644 server/server.cpp create mode 100644 server/unit.cpp diff --git a/client/base_scene.cpp b/client/base_scene.cpp new file mode 100644 index 0000000..a595114 --- /dev/null +++ b/client/base_scene.cpp @@ -0,0 +1,114 @@ +#include "base_scene.h" + +#include + +//------------------------- +//Static declarations +//------------------------- + +SDL_Surface* BaseScene::screen = nullptr; + +//------------------------- +//Public access members +//------------------------- + +BaseScene::BaseScene() { + nextScene = SceneList::CONTINUE; +} + +BaseScene::~BaseScene() { + // +} + +//------------------------- +//Program control +//------------------------- + +SDL_Surface* BaseScene::SetScreen(int w, int h, int bpp, Uint32 flags) { + if (!bpp) { + bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel; + } + + screen = SDL_SetVideoMode(w, h, bpp, flags); + + if (screen == NULL) { + throw(std::runtime_error("Failed to create the screen surface")); + } + + return screen; +} + +SDL_Surface* BaseScene::GetScreen() { + return screen; +} + +SceneList BaseScene::SetNextScene(SceneList sceneIndex) { + return nextScene = sceneIndex; +} + +SceneList BaseScene::GetNextScene() const { + return nextScene; +} + +//------------------------- +//Frame loop +//------------------------- + +void BaseScene::RunFrame() { + FrameStart(); + HandleEvents(); + Update(); + Render(screen); + SDL_Flip(screen); + FrameEnd(); +} + +//------------------------- +//Event handlers +//------------------------- + +void BaseScene::HandleEvents() { + SDL_Event event; + + while(SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + QuitEvent(); + break; + + case SDL_VIDEORESIZE: + SetScreen(event.resize.w, event.resize.h, 0, screen->flags); + break; + + case SDL_MOUSEMOTION: + MouseMotion(event.motion); + break; + + case SDL_MOUSEBUTTONDOWN: + MouseButtonDown(event.button); + break; + + case SDL_MOUSEBUTTONUP: + MouseButtonUp(event.button); + break; + + case SDL_KEYDOWN: + KeyDown(event.key); + break; + + case SDL_KEYUP: + KeyUp(event.key); + break; + +#ifdef USE_EVENT_JOYSTICK + //TODO: joystick/gamepad support +#endif + +#ifdef USE_EVENT_UNKNOWN + default: + UnknownEvent(event); + break; +#endif + }//switch + }//while +} diff --git a/client/base_scene.h b/client/base_scene.h new file mode 100644 index 0000000..b9f11ba --- /dev/null +++ b/client/base_scene.h @@ -0,0 +1,53 @@ +#ifndef BASESCENE_H_ +#define BASESCENE_H_ + +#include "scene_list.h" + +#include "SDL/SDL.h" + +class BaseScene { +public: + /* Public access members */ + BaseScene(); + virtual ~BaseScene(); + + /* Program control */ + static SDL_Surface* SetScreen(int w, int h, int bpp = 0, Uint32 flags = SDL_HWSURFACE|SDL_DOUBLEBUF); + static SDL_Surface* GetScreen(); + + SceneList SetNextScene(SceneList sceneIndex); + SceneList GetNextScene() const; + + /* Frame loop */ + virtual void RunFrame(); + +protected: + virtual void FrameStart() {} + virtual void FrameEnd() {} + virtual void Update() {} + virtual void Render(SDL_Surface* const screen) {} + + /* Event handlers */ + virtual void HandleEvents(); + + virtual void QuitEvent () { SetNextScene(SceneList::QUIT); } + virtual void MouseMotion (SDL_MouseMotionEvent const&) {} + virtual void MouseButtonDown (SDL_MouseButtonEvent const&) {} + virtual void MouseButtonUp (SDL_MouseButtonEvent const&) {} + virtual void KeyDown (SDL_KeyboardEvent const&) {} + virtual void KeyUp (SDL_KeyboardEvent const&) {} + +#ifdef USE_EVENT_JOYSTICK + //TODO: joystick/gamepad support +#endif + +#ifdef USE_EVENT_UNKNOWN + virtual void UnknownEvent (SDL_Event const&) {} +#endif + +private: + static SDL_Surface* screen; + SceneList nextScene; +}; + +#endif diff --git a/client/main.cpp b/client/main.cpp new file mode 100644 index 0000000..a3e9c86 --- /dev/null +++ b/client/main.cpp @@ -0,0 +1,20 @@ +#include "scene_manager.h" + +#include +#include + +using namespace std; + +int main(int, char**) { + SceneManager app; + try { + app.Init(); + app.Proc(); + app.Quit(); + } + catch(exception& e) { + cerr << "Fatal error: " << e.what() << endl; + return 1; + } + return 0; +} diff --git a/client/makefile b/client/makefile new file mode 100644 index 0000000..3e2110f --- /dev/null +++ b/client/makefile @@ -0,0 +1,19 @@ +CXXFLAGS+=-std=c++11 +DFLAGS=-DDEBUG +LIB=-lmingw32 -lSDL_net -lSDLmain -lSDL -lwsock32 -liphlpapi +OBJ=base_scene.o scene_manager.o main.o +SRC=test_systems.cpp + +all: debug + +release: $(OBJ) + $(CXX) $(CXXFLAGS) $(SRC) $(OBJ) $(LIB) + +debug: $(OBJ) + $(CXX) $(CXXFLAGS) $(DFLAGS) $(SRC) $(OBJ) $(LIB) + +clean: + -$(RM) *.o *.a *.exe + +unit: + $(CXX) $(CXXFLAGS) unit.cpp diff --git a/client/scene_list.h b/client/scene_list.h new file mode 100644 index 0000000..86c6b87 --- /dev/null +++ b/client/scene_list.h @@ -0,0 +1,14 @@ +#ifndef SCENELIST_H_ +#define SCENELIST_H_ + +enum class SceneList { + //these are reserved + QUIT, + CONTINUE, + FIRST, + + //custom indexes + TESTSYSTEMS, +}; + +#endif diff --git a/client/scene_manager.cpp b/client/scene_manager.cpp new file mode 100644 index 0000000..e5d6635 --- /dev/null +++ b/client/scene_manager.cpp @@ -0,0 +1,76 @@ +#include "scene_manager.h" + +#include + +//------------------------- +//Scene headers +//------------------------- + +//Add the custom scene headers here +#include "test_systems.h" + +//------------------------- +//Public access members +//------------------------- + +SceneManager::SceneManager() { + activeScene = nullptr; +} + +SceneManager::~SceneManager() { + UnloadScene(); +} + +void SceneManager::Init() { + if (SDL_Init(SDL_INIT_VIDEO)) + throw(std::runtime_error("Failed to initialize SDL")); + + BaseScene::SetScreen(800, 600); +} + +void SceneManager::Proc() { + LoadScene(SceneList::FIRST); + + //The main loop + while(activeScene->GetNextScene() != SceneList::QUIT) { + //switch scenes when necessary + if (activeScene->GetNextScene() != SceneList::CONTINUE) { + LoadScene(activeScene->GetNextScene()); + continue; + } + + //call each user defined function + activeScene->RunFrame(); + } + + UnloadScene(); +} + +void SceneManager::Quit() { + UnloadScene(); + SDL_Quit(); +} + +//------------------------- +//Private access members +//------------------------- + +void SceneManager::LoadScene(SceneList sceneIndex) { + UnloadScene(); + + switch(sceneIndex) { + //add scene creation calls here + case SceneList::FIRST: + case SceneList::TESTSYSTEMS: + activeScene = new TestSystems(); + break; + + default: + throw(std::logic_error("Failed to recognize the scene index")); + } +} + +void SceneManager::UnloadScene() { + delete activeScene; + activeScene = nullptr; +} diff --git a/client/scene_manager.h b/client/scene_manager.h new file mode 100644 index 0000000..62116e5 --- /dev/null +++ b/client/scene_manager.h @@ -0,0 +1,27 @@ +#ifndef SCENEMANAGER_H_ +#define SCENEMANAGER_H_ + +#include "scene_list.h" +#include "base_scene.h" + +#include "SDL/SDL.h" + +class SceneManager { +public: + /* Public access members */ + SceneManager(); + ~SceneManager(); + + void Init(); + void Proc(); + void Quit(); + +private: + /* Private access members */ + void LoadScene(SceneList sceneIndex); + void UnloadScene(); + + BaseScene* activeScene; +}; + +#endif diff --git a/client/test_systems.cpp b/client/test_systems.cpp new file mode 100644 index 0000000..11f994d --- /dev/null +++ b/client/test_systems.cpp @@ -0,0 +1,61 @@ +#include "test_systems.h" + +//------------------------- +//Public access members +//------------------------- + +TestSystems::TestSystems() { + // +} + +TestSystems::~TestSystems() { + // +} + +//------------------------- +//Frame loop +//------------------------- + +void TestSystems::FrameStart() { + // +} + +void TestSystems::FrameEnd() { + // +} + +void TestSystems::Update() { + // +} + +void TestSystems::Render(SDL_Surface* const screen) { + // +} + +//------------------------- +//Event handlers +//------------------------- + +void TestSystems::MouseMotion(SDL_MouseMotionEvent const& motion) { + // +} + +void TestSystems::MouseButtonDown(SDL_MouseButtonEvent const& button) { + // +} + +void TestSystems::MouseButtonUp(SDL_MouseButtonEvent const& button) { + // +} + +void TestSystems::KeyDown(SDL_KeyboardEvent const& key) { + switch(key.keysym.sym) { + case SDLK_ESCAPE: + QuitEvent(); + break; + } +} + +void TestSystems::KeyUp(SDL_KeyboardEvent const& key) { + // +} diff --git a/client/test_systems.h b/client/test_systems.h new file mode 100644 index 0000000..ada834f --- /dev/null +++ b/client/test_systems.h @@ -0,0 +1,27 @@ +#ifndef TESTSYSTEMS_H_ +#define TESTSYSTEMS_H_ + +#include "base_scene.h" + +class TestSystems : public BaseScene { +public: + /* Public access members */ + TestSystems(); + virtual ~TestSystems(); + +protected: + /* Frame loop */ + virtual void FrameStart(); + virtual void FrameEnd(); + virtual void Update(); + virtual void Render(SDL_Surface* const); + + /* Event handlers */ + virtual void MouseMotion (SDL_MouseMotionEvent const&); + virtual void MouseButtonDown (SDL_MouseButtonEvent const&); + virtual void MouseButtonUp (SDL_MouseButtonEvent const&); + virtual void KeyDown (SDL_KeyboardEvent const&); + virtual void KeyUp (SDL_KeyboardEvent const&); +}; + +#endif diff --git a/client/unit.cpp b/client/unit.cpp new file mode 100644 index 0000000..e69de29 diff --git a/docs/pseudocode.txt b/docs/pseudocode.txt new file mode 100644 index 0000000..f7d615e --- /dev/null +++ b/docs/pseudocode.txt @@ -0,0 +1,24 @@ +Client: + loops + handles input from the user + handles graphics and sound + communicates with the server (how?) + +------------------------- + +Server: + loops + accepts new connections, disconnections, and handles loss of connections + holds the positions/data of all players + +Player: + id + [graphical stuff] + position + velocity + +------------------------- + +Animations: + multiple cells in one animation (x-axis) + multiple animations in one sheet (y-axis) diff --git a/libs/SDL_net/SDL_net.h b/libs/SDL_net/SDL_net.h new file mode 100644 index 0000000..9a0dad8 --- /dev/null +++ b/libs/SDL_net/SDL_net.h @@ -0,0 +1,452 @@ +/* + SDL_net: An example cross-platform network library for use with SDL + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifndef _SDL_NET_H +#define _SDL_NET_H + +#include "SDL.h" +#include "SDL_endian.h" +#include "SDL_version.h" +#include "begin_code.h" + + + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL +*/ +#define SDL_NET_MAJOR_VERSION 1 +#define SDL_NET_MINOR_VERSION 2 +#define SDL_NET_PATCHLEVEL 8 + +/* This macro can be used to fill a version structure with the compile-time + * version of the SDL_net library. + */ +#define SDL_NET_VERSION(X) \ +{ \ + (X)->major = SDL_NET_MAJOR_VERSION; \ + (X)->minor = SDL_NET_MINOR_VERSION; \ + (X)->patch = SDL_NET_PATCHLEVEL; \ +} + +/* This function gets the version of the dynamically linked SDL_net library. + it should NOT be used to fill a version structure, instead you should + use the SDL_NET_VERSION() macro. + */ +extern DECLSPEC const SDL_version * SDLCALL SDLNet_Linked_Version(void); + +/* Initialize/Cleanup the network API + SDL must be initialized before calls to functions in this library, + because this library uses utility functions from the SDL library. +*/ +extern DECLSPEC int SDLCALL SDLNet_Init(void); +extern DECLSPEC void SDLCALL SDLNet_Quit(void); + +/***********************************************************************/ +/* IPv4 hostname resolution API */ +/***********************************************************************/ + +typedef struct { + Uint32 host; /* 32-bit IPv4 host address */ + Uint16 port; /* 16-bit protocol port */ +} IPaddress; + +/* Resolve a host name and port to an IP address in network form. + If the function succeeds, it will return 0. + If the host couldn't be resolved, the host portion of the returned + address will be INADDR_NONE, and the function will return -1. + If 'host' is NULL, the resolved host will be set to INADDR_ANY. + */ +#ifndef INADDR_ANY +#define INADDR_ANY 0x00000000 +#endif +#ifndef INADDR_NONE +#define INADDR_NONE 0xFFFFFFFF +#endif +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 +#endif +#ifndef INADDR_BROADCAST +#define INADDR_BROADCAST 0xFFFFFFFF +#endif +extern DECLSPEC int SDLCALL SDLNet_ResolveHost(IPaddress *address, const char *host, Uint16 port); + +/* Resolve an ip address to a host name in canonical form. + If the ip couldn't be resolved, this function returns NULL, + otherwise a pointer to a static buffer containing the hostname + is returned. Note that this function is not thread-safe. +*/ +extern DECLSPEC const char * SDLCALL SDLNet_ResolveIP(const IPaddress *ip); + +/* Get the addresses of network interfaces on this system. + This returns the number of addresses saved in 'addresses' + */ +extern DECLSPEC int SDLCALL SDLNet_GetLocalAddresses(IPaddress *addresses, int maxcount); + +/***********************************************************************/ +/* TCP network API */ +/***********************************************************************/ + +typedef struct _TCPsocket *TCPsocket; + +/* Open a TCP network socket + If ip.host is INADDR_NONE or INADDR_ANY, this creates a local server + socket on the given port, otherwise a TCP connection to the remote + host and port is attempted. The address passed in should already be + swapped to network byte order (addresses returned from + SDLNet_ResolveHost() are already in the correct form). + The newly created socket is returned, or NULL if there was an error. +*/ +extern DECLSPEC TCPsocket SDLCALL SDLNet_TCP_Open(IPaddress *ip); + +/* Accept an incoming connection on the given server socket. + The newly created socket is returned, or NULL if there was an error. +*/ +extern DECLSPEC TCPsocket SDLCALL SDLNet_TCP_Accept(TCPsocket server); + +/* Get the IP address of the remote system associated with the socket. + If the socket is a server socket, this function returns NULL. +*/ +extern DECLSPEC IPaddress * SDLCALL SDLNet_TCP_GetPeerAddress(TCPsocket sock); + +/* Send 'len' bytes of 'data' over the non-server socket 'sock' + This function returns the actual amount of data sent. If the return value + is less than the amount of data sent, then either the remote connection was + closed, or an unknown socket error occurred. +*/ +extern DECLSPEC int SDLCALL SDLNet_TCP_Send(TCPsocket sock, const void *data, + int len); + +/* Receive up to 'maxlen' bytes of data over the non-server socket 'sock', + and store them in the buffer pointed to by 'data'. + This function returns the actual amount of data received. If the return + value is less than or equal to zero, then either the remote connection was + closed, or an unknown socket error occurred. +*/ +extern DECLSPEC int SDLCALL SDLNet_TCP_Recv(TCPsocket sock, void *data, int maxlen); + +/* Close a TCP network socket */ +extern DECLSPEC void SDLCALL SDLNet_TCP_Close(TCPsocket sock); + + +/***********************************************************************/ +/* UDP network API */ +/***********************************************************************/ + +/* The maximum channels on a a UDP socket */ +#define SDLNET_MAX_UDPCHANNELS 32 +/* The maximum addresses bound to a single UDP socket channel */ +#define SDLNET_MAX_UDPADDRESSES 4 + +typedef struct _UDPsocket *UDPsocket; +typedef struct { + int channel; /* The src/dst channel of the packet */ + Uint8 *data; /* The packet data */ + int len; /* The length of the packet data */ + int maxlen; /* The size of the data buffer */ + int status; /* packet status after sending */ + IPaddress address; /* The source/dest address of an incoming/outgoing packet */ +} UDPpacket; + +/* Allocate/resize/free a single UDP packet 'size' bytes long. + The new packet is returned, or NULL if the function ran out of memory. + */ +extern DECLSPEC UDPpacket * SDLCALL SDLNet_AllocPacket(int size); +extern DECLSPEC int SDLCALL SDLNet_ResizePacket(UDPpacket *packet, int newsize); +extern DECLSPEC void SDLCALL SDLNet_FreePacket(UDPpacket *packet); + +/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, + each 'size' bytes long. + A pointer to the first packet in the array is returned, or NULL if the + function ran out of memory. + */ +extern DECLSPEC UDPpacket ** SDLCALL SDLNet_AllocPacketV(int howmany, int size); +extern DECLSPEC void SDLCALL SDLNet_FreePacketV(UDPpacket **packetV); + + +/* Open a UDP network socket + If 'port' is non-zero, the UDP socket is bound to a local port. + The 'port' should be given in native byte order, but is used + internally in network (big endian) byte order, in addresses, etc. + This allows other systems to send to this socket via a known port. +*/ +extern DECLSPEC UDPsocket SDLCALL SDLNet_UDP_Open(Uint16 port); + +/* Set the percentage of simulated packet loss for packets sent on the socket. +*/ +extern DECLSPEC void SDLCALL SDLNet_UDP_SetPacketLoss(UDPsocket sock, int percent); + +/* Bind the address 'address' to the requested channel on the UDP socket. + If the channel is -1, then the first unbound channel will be bound with + the given address as it's primary address. + If the channel is already bound, this new address will be added to the + list of valid source addresses for packets arriving on the channel. + If the channel is not already bound, then the address becomes the primary + address, to which all outbound packets on the channel are sent. + This function returns the channel which was bound, or -1 on error. +*/ +extern DECLSPEC int SDLCALL SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address); + +/* Unbind all addresses from the given channel */ +extern DECLSPEC void SDLCALL SDLNet_UDP_Unbind(UDPsocket sock, int channel); + +/* Get the primary IP address of the remote system associated with the + socket and channel. If the channel is -1, then the primary IP port + of the UDP socket is returned -- this is only meaningful for sockets + opened with a specific port. + If the channel is not bound and not -1, this function returns NULL. + */ +extern DECLSPEC IPaddress * SDLCALL SDLNet_UDP_GetPeerAddress(UDPsocket sock, int channel); + +/* Send a vector of packets to the the channels specified within the packet. + If the channel specified in the packet is -1, the packet will be sent to + the address in the 'src' member of the packet. + Each packet will be updated with the status of the packet after it has + been sent, -1 if the packet send failed. + This function returns the number of packets sent. +*/ +extern DECLSPEC int SDLCALL SDLNet_UDP_SendV(UDPsocket sock, UDPpacket **packets, int npackets); + +/* Send a single packet to the specified channel. + If the channel specified in the packet is -1, the packet will be sent to + the address in the 'src' member of the packet. + The packet will be updated with the status of the packet after it has + been sent. + This function returns 1 if the packet was sent, or 0 on error. + + NOTE: + The maximum size of the packet is limited by the MTU (Maximum Transfer Unit) + of the transport medium. It can be as low as 250 bytes for some PPP links, + and as high as 1500 bytes for ethernet. +*/ +extern DECLSPEC int SDLCALL SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet); + +/* Receive a vector of pending packets from the UDP socket. + The returned packets contain the source address and the channel they arrived + on. If they did not arrive on a bound channel, the the channel will be set + to -1. + The channels are checked in highest to lowest order, so if an address is + bound to multiple channels, the highest channel with the source address + bound will be returned. + This function returns the number of packets read from the network, or -1 + on error. This function does not block, so can return 0 packets pending. +*/ +extern DECLSPEC int SDLCALL SDLNet_UDP_RecvV(UDPsocket sock, UDPpacket **packets); + +/* Receive a single packet from the UDP socket. + The returned packet contains the source address and the channel it arrived + on. If it did not arrive on a bound channel, the the channel will be set + to -1. + The channels are checked in highest to lowest order, so if an address is + bound to multiple channels, the highest channel with the source address + bound will be returned. + This function returns the number of packets read from the network, or -1 + on error. This function does not block, so can return 0 packets pending. +*/ +extern DECLSPEC int SDLCALL SDLNet_UDP_Recv(UDPsocket sock, UDPpacket *packet); + +/* Close a UDP network socket */ +extern DECLSPEC void SDLCALL SDLNet_UDP_Close(UDPsocket sock); + + +/***********************************************************************/ +/* Hooks for checking sockets for available data */ +/***********************************************************************/ + +typedef struct _SDLNet_SocketSet *SDLNet_SocketSet; + +/* Any network socket can be safely cast to this socket type */ +typedef struct _SDLNet_GenericSocket { + int ready; +} *SDLNet_GenericSocket; + +/* Allocate a socket set for use with SDLNet_CheckSockets() + This returns a socket set for up to 'maxsockets' sockets, or NULL if + the function ran out of memory. + */ +extern DECLSPEC SDLNet_SocketSet SDLCALL SDLNet_AllocSocketSet(int maxsockets); + +/* Add a socket to a set of sockets to be checked for available data */ +#define SDLNet_TCP_AddSocket(set, sock) \ + SDLNet_AddSocket(set, SDL_reinterpret_cast(SDLNet_GenericSocket, sock)) +#define SDLNet_UDP_AddSocket(set, sock) \ + SDLNet_AddSocket(set, SDL_reinterpret_cast(SDLNet_GenericSocket, sock)) +extern DECLSPEC int SDLCALL SDLNet_AddSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock); + +/* Remove a socket from a set of sockets to be checked for available data */ +#define SDLNet_TCP_DelSocket(set, sock) \ + SDLNet_DelSocket(set, SDL_reinterpret_cast(SDLNet_GenericSocket, sock)) +#define SDLNet_UDP_DelSocket(set, sock) \ + SDLNet_DelSocket(set, SDL_reinterpret_cast(SDLNet_GenericSocket, sock)) +extern DECLSPEC int SDLCALL SDLNet_DelSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock); + +/* This function checks to see if data is available for reading on the + given set of sockets. If 'timeout' is 0, it performs a quick poll, + otherwise the function returns when either data is available for + reading, or the timeout in milliseconds has elapsed, which ever occurs + first. This function returns the number of sockets ready for reading, + or -1 if there was an error with the select() system call. +*/ +extern DECLSPEC int SDLCALL SDLNet_CheckSockets(SDLNet_SocketSet set, Uint32 timeout); + +/* After calling SDLNet_CheckSockets(), you can use this function on a + socket that was in the socket set, to find out if data is available + for reading. +*/ +#define SDLNet_SocketReady(sock) \ + ((sock != NULL) && SDL_reinterpret_cast(SDLNet_GenericSocket, sock)->ready) + +/* Free a set of sockets allocated by SDL_NetAllocSocketSet() */ +extern DECLSPEC void SDLCALL SDLNet_FreeSocketSet(SDLNet_SocketSet set); + + +/***********************************************************************/ +/* Platform-independent data conversion functions */ +/***********************************************************************/ + +/* Write a 16/32 bit value to network packet buffer */ +extern DECLSPEC void SDLCALL SDLNet_Write16(Uint16 value, void *area); +extern DECLSPEC void SDLCALL SDLNet_Write32(Uint32 value, void *area); + +/* Read a 16/32 bit value from network packet buffer */ +extern DECLSPEC Uint16 SDLCALL SDLNet_Read16(void *area); +extern DECLSPEC Uint32 SDLCALL SDLNet_Read32(void *area); + +/***********************************************************************/ +/* Error reporting functions */ +/***********************************************************************/ + +/* We'll use SDL's functions for error reporting */ +#define SDLNet_SetError SDL_SetError +#define SDLNet_GetError SDL_GetError + +/* I'm eventually going to try to disentangle SDL_net from SDL, thus making + SDL_net an independent X-platform networking toolkit. Not today though.... + +extern no_parse_DECLSPEC void SDLCALL SDLNet_SetError(const char *fmt, ...); +extern no_parse_DECLSPEC char * SDLCALL SDLNet_GetError(void); +*/ + + +/* Inline macro functions to read/write network data */ + +/* Warning, some systems have data access alignment restrictions */ +#if defined(sparc) || defined(mips) +#define SDL_DATA_ALIGNED 1 +#endif +#ifndef SDL_DATA_ALIGNED +#define SDL_DATA_ALIGNED 0 +#endif + +/* Write a 16 bit value to network packet buffer */ +#if !SDL_DATA_ALIGNED +#define SDLNet_Write16(value, areap) \ + (*SDL_reinterpret_cast(Uint16 *, areap) = SDL_SwapBE16(value)) +#else +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define SDLNet_Write16(value, areap) \ +do \ +{ \ + Uint8 *area = SDL_reinterpret_cast(Uint8 *, areap); \ + area[0] = (value >> 8) & 0xFF; \ + area[1] = value & 0xFF; \ +} while ( 0 ) +#else +#define SDLNet_Write16(value, areap) \ +do \ +{ \ + Uint8 *area = SDL_reinterpret_cast(Uint8 *, areap); \ + area[1] = (value >> 8) & 0xFF; \ + area[0] = value & 0xFF; \ +} while ( 0 ) +#endif +#endif /* !SDL_DATA_ALIGNED */ + +/* Write a 32 bit value to network packet buffer */ +#if !SDL_DATA_ALIGNED +#define SDLNet_Write32(value, areap) \ + *SDL_reinterpret_cast(Uint32 *, areap) = SDL_SwapBE32(value); +#else +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define SDLNet_Write32(value, areap) \ +do \ +{ \ + Uint8 *area = SDL_reinterpret_cast(Uint8 *, areap); \ + area[0] = (value >> 24) & 0xFF; \ + area[1] = (value >> 16) & 0xFF; \ + area[2] = (value >> 8) & 0xFF; \ + area[3] = value & 0xFF; \ +} while ( 0 ) +#else +#define SDLNet_Write32(value, areap) \ +do \ +{ \ + Uint8 *area = SDL_reinterpret_cast(Uint8 *, areap); \ + area[3] = (value >> 24) & 0xFF; \ + area[2] = (value >> 16) & 0xFF; \ + area[1] = (value >> 8) & 0xFF; \ + area[0] = value & 0xFF; \ +} while ( 0 ) +#endif +#endif /* !SDL_DATA_ALIGNED */ + +/* Read a 16 bit value from network packet buffer */ +#if !SDL_DATA_ALIGNED +#define SDLNet_Read16(areap) \ + (SDL_SwapBE16(*SDL_reinterpret_cast(Uint16 *, areap))) +#else +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define SDLNet_Read16(areap) \ + (((SDL_reinterpret_cast(Uint8 *, areap))[0] << 8) | (SDL_reinterpret_cast(Uint8 *, areap))[1] << 0) +#else +#define SDLNet_Read16(areap) \ + (((SDL_reinterpret_cast(Uint8 *, areap))[1] << 8) | (SDL_reinterpret_cast(Uint8 *, areap))[0] << 0) +#endif +#endif /* !SDL_DATA_ALIGNED */ + +/* Read a 32 bit value from network packet buffer */ +#if !SDL_DATA_ALIGNED +#define SDLNet_Read32(areap) \ + (SDL_SwapBE32(*SDL_reinterpret_cast(Uint32 *, areap))) +#else +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define SDLNet_Read32(areap) \ + (((SDL_reinterpret_cast(Uint8 *, areap))[0] << 24) | ((SDL_reinterpret_cast(Uint8 *, areap))[1] << 16) | \ + ((SDL_reinterpret_cast(Uint8 *, areap))[2] << 8) | (SDL_reinterpret_cast(Uint8 *, areap))[3] << 0) +#else +#define SDLNet_Read32(areap) \ + (((SDL_reinterpret_cast(Uint8 *, areap))[3] << 24) | ((SDL_reinterpret_cast(Uint8 *, areap))[2] << 16) | \ + ((SDL_reinterpret_cast(Uint8 *, areap))[1] << 8) | (SDL_reinterpret_cast(Uint8 *, areap))[0] << 0) +#endif +#endif /* !SDL_DATA_ALIGNED */ + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_NET_H */ diff --git a/libs/SDL_net/SDLnet.c b/libs/SDL_net/SDLnet.c new file mode 100644 index 0000000..a081f70 --- /dev/null +++ b/libs/SDL_net/SDLnet.c @@ -0,0 +1,269 @@ +/* + SDL_net: An example cross-platform network library for use with SDL + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#include + +#include "SDL_endian.h" + +#include "SDLnetsys.h" +#include "SDL_net.h" + + +const SDL_version *SDLNet_Linked_Version(void) +{ + static SDL_version linked_version; + SDL_NET_VERSION(&linked_version); + return(&linked_version); +} + +/* Since the UNIX/Win32/BeOS code is so different from MacOS, + we'll just have two completely different sections here. +*/ +static int SDLNet_started = 0; + +#ifndef __USE_W32_SOCKETS +#include +#endif + +#ifndef __USE_W32_SOCKETS + +int SDLNet_GetLastError(void) +{ + return errno; +} + +void SDLNet_SetLastError(int err) +{ + errno = err; +} + +#endif + +/* Initialize/Cleanup the network API */ +int SDLNet_Init(void) +{ + if ( !SDLNet_started ) { +#ifdef __USE_W32_SOCKETS + /* Start up the windows networking */ + WORD version_wanted = MAKEWORD(1,1); + WSADATA wsaData; + + if ( WSAStartup(version_wanted, &wsaData) != 0 ) { + SDLNet_SetError("Couldn't initialize Winsock 1.1\n"); + return(-1); + } +#else + /* SIGPIPE is generated when a remote socket is closed */ + void (*handler)(int); + handler = signal(SIGPIPE, SIG_IGN); + if ( handler != SIG_DFL ) { + signal(SIGPIPE, handler); + } +#endif + } + ++SDLNet_started; + return(0); +} +void SDLNet_Quit(void) +{ + if ( SDLNet_started == 0 ) { + return; + } + if ( --SDLNet_started == 0 ) { +#ifdef __USE_W32_SOCKETS + /* Clean up windows networking */ + if ( WSACleanup() == SOCKET_ERROR ) { + if ( WSAGetLastError() == WSAEINPROGRESS ) { +#ifndef _WIN32_WCE + WSACancelBlockingCall(); +#endif + WSACleanup(); + } + } +#else + /* Restore the SIGPIPE handler */ + void (*handler)(int); + handler = signal(SIGPIPE, SIG_DFL); + if ( handler != SIG_IGN ) { + signal(SIGPIPE, handler); + } +#endif + } +} + +/* Resolve a host name and port to an IP address in network form */ +int SDLNet_ResolveHost(IPaddress *address, const char *host, Uint16 port) +{ + int retval = 0; + + /* Perform the actual host resolution */ + if ( host == NULL ) { + address->host = INADDR_ANY; + } else { + address->host = inet_addr(host); + if ( address->host == INADDR_NONE ) { + struct hostent *hp; + + hp = gethostbyname(host); + if ( hp ) { + memcpy(&address->host,hp->h_addr,hp->h_length); + } else { + retval = -1; + } + } + } + address->port = SDL_SwapBE16(port); + + /* Return the status */ + return(retval); +} + +/* Resolve an ip address to a host name in canonical form. + If the ip couldn't be resolved, this function returns NULL, + otherwise a pointer to a static buffer containing the hostname + is returned. Note that this function is not thread-safe. +*/ +/* Written by Miguel Angel Blanch. + * Main Programmer of Arianne RPG. + * http://come.to/arianne_rpg + */ +const char *SDLNet_ResolveIP(const IPaddress *ip) +{ + struct hostent *hp; + struct in_addr in; + + hp = gethostbyaddr((const char *)&ip->host, sizeof(ip->host), AF_INET); + if ( hp != NULL ) { + return hp->h_name; + } + + in.s_addr = ip->host; + return inet_ntoa(in); +} + +int SDLNet_GetLocalAddresses(IPaddress *addresses, int maxcount) +{ + int count = 0; +#ifdef SIOCGIFCONF +/* Defined on Mac OS X */ +#ifndef _SIZEOF_ADDR_IFREQ +#define _SIZEOF_ADDR_IFREQ sizeof +#endif + SOCKET sock; + struct ifconf conf; + char data[4096]; + struct ifreq *ifr; + struct sockaddr_in *sock_addr; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if ( sock == INVALID_SOCKET ) { + return 0; + } + + conf.ifc_len = sizeof(data); + conf.ifc_buf = (caddr_t) data; + if ( ioctl(sock, SIOCGIFCONF, &conf) < 0 ) { + closesocket(sock); + return 0; + } + + ifr = (struct ifreq*)data; + while ((char*)ifr < data+conf.ifc_len) { + if (ifr->ifr_addr.sa_family == AF_INET) { + if (count < maxcount) { + sock_addr = (struct sockaddr_in*)&ifr->ifr_addr; + addresses[count].host = sock_addr->sin_addr.s_addr; + addresses[count].port = sock_addr->sin_port; + } + ++count; + } + ifr = (struct ifreq*)((char*)ifr + _SIZEOF_ADDR_IFREQ(*ifr)); + } + closesocket(sock); +#elif defined(__WIN32__) + PIP_ADAPTER_INFO pAdapterInfo; + PIP_ADAPTER_INFO pAdapter; + PIP_ADDR_STRING pAddress; + DWORD dwRetVal = 0; + ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); + + pAdapterInfo = (IP_ADAPTER_INFO *) SDL_malloc(sizeof (IP_ADAPTER_INFO)); + if (pAdapterInfo == NULL) { + return 0; + } + + if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == ERROR_BUFFER_OVERFLOW) { + pAdapterInfo = (IP_ADAPTER_INFO *) SDL_realloc(pAdapterInfo, ulOutBufLen); + if (pAdapterInfo == NULL) { + return 0; + } + dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); + } + + if (dwRetVal == NO_ERROR) { + for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) { + for (pAddress = &pAdapterInfo->IpAddressList; pAddress; pAddress = pAddress->Next) { + if (count < maxcount) { + addresses[count].host = inet_addr(pAddress->IpAddress.String); + addresses[count].port = 0; + } + ++count; + } + } + } + SDL_free(pAdapterInfo); +#endif + return count; +} + +#if !SDL_DATA_ALIGNED /* function versions for binary compatibility */ + +/* Write a 16 bit value to network packet buffer */ +#undef SDLNet_Write16 +void SDLNet_Write16(Uint16 value, void *areap) +{ + (*(Uint16 *)(areap) = SDL_SwapBE16(value)); +} + +/* Write a 32 bit value to network packet buffer */ +#undef SDLNet_Write32 +void SDLNet_Write32(Uint32 value, void *areap) +{ + *(Uint32 *)(areap) = SDL_SwapBE32(value); +} + +/* Read a 16 bit value from network packet buffer */ +#undef SDLNet_Read16 +Uint16 SDLNet_Read16(void *areap) +{ + return (SDL_SwapBE16(*(Uint16 *)(areap))); +} + +/* Read a 32 bit value from network packet buffer */ +#undef SDLNet_Read32 +Uint32 SDLNet_Read32(void *areap) +{ + return (SDL_SwapBE32(*(Uint32 *)(areap))); +} + +#endif /* !SDL_DATA_ALIGNED */ diff --git a/libs/SDL_net/SDLnetTCP.c b/libs/SDL_net/SDLnetTCP.c new file mode 100644 index 0000000..e68f208 --- /dev/null +++ b/libs/SDL_net/SDLnetTCP.c @@ -0,0 +1,295 @@ +/* + SDL_net: An example cross-platform network library for use with SDL + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#include "SDLnetsys.h" +#include "SDL_net.h" + +/* The network API for TCP sockets */ + +/* Since the UNIX/Win32/BeOS code is so different from MacOS, + we'll just have two completely different sections here. +*/ + +struct _TCPsocket { + int ready; + SOCKET channel; + IPaddress remoteAddress; + IPaddress localAddress; + int sflag; +}; + +/* Open a TCP network socket + If 'remote' is NULL, this creates a local server socket on the given port, + otherwise a TCP connection to the remote host and port is attempted. + The newly created socket is returned, or NULL if there was an error. +*/ +TCPsocket SDLNet_TCP_Open(IPaddress *ip) +{ + TCPsocket sock; + struct sockaddr_in sock_addr; + + /* Allocate a TCP socket structure */ + sock = (TCPsocket)malloc(sizeof(*sock)); + if ( sock == NULL ) { + SDLNet_SetError("Out of memory"); + goto error_return; + } + + /* Open the socket */ + sock->channel = socket(AF_INET, SOCK_STREAM, 0); + if ( sock->channel == INVALID_SOCKET ) { + SDLNet_SetError("Couldn't create socket"); + goto error_return; + } + + /* Connect to remote, or bind locally, as appropriate */ + if ( (ip->host != INADDR_NONE) && (ip->host != INADDR_ANY) ) { + + // ######### Connecting to remote + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = ip->host; + sock_addr.sin_port = ip->port; + + /* Connect to the remote host */ + if ( connect(sock->channel, (struct sockaddr *)&sock_addr, + sizeof(sock_addr)) == SOCKET_ERROR ) { + SDLNet_SetError("Couldn't connect to remote host"); + goto error_return; + } + sock->sflag = 0; + } else { + + // ########## Binding locally + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = INADDR_ANY; + sock_addr.sin_port = ip->port; + +/* + * Windows gets bad mojo with SO_REUSEADDR: + * http://www.devolution.com/pipermail/sdl/2005-September/070491.html + * --ryan. + */ +#ifndef WIN32 + /* allow local address reuse */ + { int yes = 1; + setsockopt(sock->channel, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); + } +#endif + + /* Bind the socket for listening */ + if ( bind(sock->channel, (struct sockaddr *)&sock_addr, + sizeof(sock_addr)) == SOCKET_ERROR ) { + SDLNet_SetError("Couldn't bind to local port"); + goto error_return; + } + if ( listen(sock->channel, 5) == SOCKET_ERROR ) { + SDLNet_SetError("Couldn't listen to local port"); + goto error_return; + } + + /* Set the socket to non-blocking mode for accept() */ +#if defined(__BEOS__) && defined(SO_NONBLOCK) + /* On BeOS r5 there is O_NONBLOCK but it's for files only */ + { + long b = 1; + setsockopt(sock->channel, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); + } +#elif defined(O_NONBLOCK) + { + fcntl(sock->channel, F_SETFL, O_NONBLOCK); + } +#elif defined(WIN32) + { + unsigned long mode = 1; + ioctlsocket (sock->channel, FIONBIO, &mode); + } +#elif defined(__OS2__) + { + int dontblock = 1; + ioctl(sock->channel, FIONBIO, &dontblock); + } +#else +#warning How do we set non-blocking mode on other operating systems? +#endif + sock->sflag = 1; + } + sock->ready = 0; + +#ifdef TCP_NODELAY + /* Set the nodelay TCP option for real-time games */ + { int yes = 1; + setsockopt(sock->channel, IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(yes)); + } +#endif /* TCP_NODELAY */ + + /* Fill in the channel host address */ + sock->remoteAddress.host = sock_addr.sin_addr.s_addr; + sock->remoteAddress.port = sock_addr.sin_port; + + /* The socket is ready */ + return(sock); + +error_return: + SDLNet_TCP_Close(sock); + return(NULL); +} + +/* Accept an incoming connection on the given server socket. + The newly created socket is returned, or NULL if there was an error. +*/ +TCPsocket SDLNet_TCP_Accept(TCPsocket server) +{ + TCPsocket sock; + struct sockaddr_in sock_addr; + socklen_t sock_alen; + + /* Only server sockets can accept */ + if ( ! server->sflag ) { + SDLNet_SetError("Only server sockets can accept()"); + return(NULL); + } + server->ready = 0; + + /* Allocate a TCP socket structure */ + sock = (TCPsocket)malloc(sizeof(*sock)); + if ( sock == NULL ) { + SDLNet_SetError("Out of memory"); + goto error_return; + } + + /* Accept a new TCP connection on a server socket */ + sock_alen = sizeof(sock_addr); + sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr, + &sock_alen); + if ( sock->channel == INVALID_SOCKET ) { + SDLNet_SetError("accept() failed"); + goto error_return; + } +#ifdef WIN32 + { + /* passing a zero value, socket mode set to block on */ + unsigned long mode = 0; + ioctlsocket (sock->channel, FIONBIO, &mode); + } +#elif defined(O_NONBLOCK) + { + int flags = fcntl(sock->channel, F_GETFL, 0); + fcntl(sock->channel, F_SETFL, flags & ~O_NONBLOCK); + } +#endif /* WIN32 */ + sock->remoteAddress.host = sock_addr.sin_addr.s_addr; + sock->remoteAddress.port = sock_addr.sin_port; + + sock->sflag = 0; + sock->ready = 0; + + /* The socket is ready */ + return(sock); + +error_return: + SDLNet_TCP_Close(sock); + return(NULL); +} + +/* Get the IP address of the remote system associated with the socket. + If the socket is a server socket, this function returns NULL. +*/ +IPaddress *SDLNet_TCP_GetPeerAddress(TCPsocket sock) +{ + if ( sock->sflag ) { + return(NULL); + } + return(&sock->remoteAddress); +} + +/* Send 'len' bytes of 'data' over the non-server socket 'sock' + This function returns the actual amount of data sent. If the return value + is less than the amount of data sent, then either the remote connection was + closed, or an unknown socket error occurred. +*/ +int SDLNet_TCP_Send(TCPsocket sock, const void *datap, int len) +{ + const Uint8 *data = (const Uint8 *)datap; /* For pointer arithmetic */ + int sent, left; + + /* Server sockets are for accepting connections only */ + if ( sock->sflag ) { + SDLNet_SetError("Server sockets cannot send"); + return(-1); + } + + /* Keep sending data until it's sent or an error occurs */ + left = len; + sent = 0; + SDLNet_SetLastError(0); + do { + len = send(sock->channel, (const char *) data, left, 0); + if ( len > 0 ) { + sent += len; + left -= len; + data += len; + } + } while ( (left > 0) && ((len > 0) || (SDLNet_GetLastError() == EINTR)) ); + + return(sent); +} + +/* Receive up to 'maxlen' bytes of data over the non-server socket 'sock', + and store them in the buffer pointed to by 'data'. + This function returns the actual amount of data received. If the return + value is less than or equal to zero, then either the remote connection was + closed, or an unknown socket error occurred. +*/ +int SDLNet_TCP_Recv(TCPsocket sock, void *data, int maxlen) +{ + int len; + + /* Server sockets are for accepting connections only */ + if ( sock->sflag ) { + SDLNet_SetError("Server sockets cannot receive"); + return(-1); + } + + SDLNet_SetLastError(0); + do { + len = recv(sock->channel, (char *) data, maxlen, 0); + } while ( SDLNet_GetLastError() == EINTR ); + + sock->ready = 0; + return(len); +} + +/* Close a TCP network socket */ +void SDLNet_TCP_Close(TCPsocket sock) +{ + if ( sock != NULL ) { + if ( sock->channel != INVALID_SOCKET ) { + closesocket(sock->channel); + } + free(sock); + } +} diff --git a/libs/SDL_net/SDLnetUDP.c b/libs/SDL_net/SDLnetUDP.c new file mode 100644 index 0000000..b81c00e --- /dev/null +++ b/libs/SDL_net/SDLnetUDP.c @@ -0,0 +1,528 @@ +/* + SDL_net: An example cross-platform network library for use with SDL + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#include "SDLnetsys.h" +#include "SDL_net.h" + +#ifdef __WIN32__ +#define srandom srand +#define random rand +#endif + +struct UDP_channel { + int numbound; + IPaddress address[SDLNET_MAX_UDPADDRESSES]; +}; + +struct _UDPsocket { + int ready; + SOCKET channel; + IPaddress address; + + struct UDP_channel binding[SDLNET_MAX_UDPCHANNELS]; + + /* For debugging purposes */ + int packetloss; +}; + +/* Allocate/free a single UDP packet 'size' bytes long. + The new packet is returned, or NULL if the function ran out of memory. + */ +extern UDPpacket *SDLNet_AllocPacket(int size) +{ + UDPpacket *packet; + int error; + + + error = 1; + packet = (UDPpacket *)malloc(sizeof(*packet)); + if ( packet != NULL ) { + packet->maxlen = size; + packet->data = (Uint8 *)malloc(size); + if ( packet->data != NULL ) { + error = 0; + } + } + if ( error ) { + SDLNet_SetError("Out of memory"); + SDLNet_FreePacket(packet); + packet = NULL; + } + return(packet); +} +int SDLNet_ResizePacket(UDPpacket *packet, int newsize) +{ + Uint8 *newdata; + + newdata = (Uint8 *)malloc(newsize); + if ( newdata != NULL ) { + free(packet->data); + packet->data = newdata; + packet->maxlen = newsize; + } + return(packet->maxlen); +} +extern void SDLNet_FreePacket(UDPpacket *packet) +{ + if ( packet ) { + if ( packet->data ) + free(packet->data); + free(packet); + } +} + +/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, + each 'size' bytes long. + A pointer to the packet array is returned, or NULL if the function ran out + of memory. + */ +UDPpacket **SDLNet_AllocPacketV(int howmany, int size) +{ + UDPpacket **packetV; + + packetV = (UDPpacket **)malloc((howmany+1)*sizeof(*packetV)); + if ( packetV != NULL ) { + int i; + for ( i=0; ichannel = socket(AF_INET, SOCK_DGRAM, 0); + if ( sock->channel == INVALID_SOCKET ) + { + SDLNet_SetError("Couldn't create socket"); + goto error_return; + } + + /* Bind locally, if appropriate */ + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = INADDR_ANY; + sock_addr.sin_port = SDL_SwapBE16(port); + + /* Bind the socket for listening */ + if ( bind(sock->channel, (struct sockaddr *)&sock_addr, + sizeof(sock_addr)) == SOCKET_ERROR ) { + SDLNet_SetError("Couldn't bind to local port"); + goto error_return; + } + + /* Get the bound address and port */ + sock_len = sizeof(sock_addr); + if ( getsockname(sock->channel, (struct sockaddr *)&sock_addr, &sock_len) < 0 ) { +perror("getsockname"); + SDLNet_SetError("Couldn't get socket address"); + goto error_return; + } + + /* Fill in the channel host address */ + sock->address.host = sock_addr.sin_addr.s_addr; + sock->address.port = sock_addr.sin_port; + +#ifdef SO_BROADCAST + /* Allow LAN broadcasts with the socket */ + { int yes = 1; + setsockopt(sock->channel, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes)); + } +#endif +#ifdef IP_ADD_MEMBERSHIP + /* Receive LAN multicast packets on 224.0.0.1 + This automatically works on Mac OS X, Linux and BSD, but needs + this code on Windows. + */ + /* A good description of multicast can be found here: + http://www.docs.hp.com/en/B2355-90136/ch05s05.html + */ + /* FIXME: Add support for joining arbitrary groups to the API */ + { + struct ip_mreq g; + + g.imr_multiaddr.s_addr = inet_addr("224.0.0.1"); + g.imr_interface.s_addr = INADDR_ANY; + setsockopt(sock->channel, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char*)&g, sizeof(g)); + } +#endif + + /* The socket is ready */ + + return(sock); + +error_return: + SDLNet_UDP_Close(sock); + + return(NULL); +} + +void SDLNet_UDP_SetPacketLoss(UDPsocket sock, int percent) +{ + /* FIXME: We may want this behavior to be reproducible + but there isn't a portable reentrant random + number generator with good randomness. + */ + srandom(SDL_GetTicks()); + + if (percent < 0) { + percent = 0; + } else if (percent > 100) { + percent = 100; + } + sock->packetloss = percent; +} + +/* Verify that the channel is in the valid range */ +static int ValidChannel(int channel) +{ + if ( (channel < 0) || (channel >= SDLNET_MAX_UDPCHANNELS) ) { + SDLNet_SetError("Invalid channel"); + return(0); + } + return(1); +} + +/* Bind the address 'address' to the requested channel on the UDP socket. + If the channel is -1, then the first unbound channel will be bound with + the given address as it's primary address. + If the channel is already bound, this new address will be added to the + list of valid source addresses for packets arriving on the channel. + If the channel is not already bound, then the address becomes the primary + address, to which all outbound packets on the channel are sent. + This function returns the channel which was bound, or -1 on error. +*/ +int SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address) +{ + struct UDP_channel *binding; + + if ( sock == NULL ) { + SDLNet_SetError("Passed a NULL socket"); + return(-1); + } + + if ( channel == -1 ) { + for ( channel=0; channel < SDLNET_MAX_UDPCHANNELS; ++channel ) { + binding = &sock->binding[channel]; + if ( binding->numbound < SDLNET_MAX_UDPADDRESSES ) { + break; + } + } + } else { + if ( ! ValidChannel(channel) ) { + return(-1); + } + binding = &sock->binding[channel]; + } + if ( binding->numbound == SDLNET_MAX_UDPADDRESSES ) { + SDLNet_SetError("No room for new addresses"); + return(-1); + } + binding->address[binding->numbound++] = *address; + return(channel); +} + +/* Unbind all addresses from the given channel */ +void SDLNet_UDP_Unbind(UDPsocket sock, int channel) +{ + if ( (channel >= 0) && (channel < SDLNET_MAX_UDPCHANNELS) ) { + sock->binding[channel].numbound = 0; + } +} + +/* Get the primary IP address of the remote system associated with the + socket and channel. + If the channel is not bound, this function returns NULL. + */ +IPaddress *SDLNet_UDP_GetPeerAddress(UDPsocket sock, int channel) +{ + IPaddress *address; + + address = NULL; + switch (channel) { + case -1: + /* Return the actual address of the socket */ + address = &sock->address; + break; + default: + /* Return the address of the bound channel */ + if ( ValidChannel(channel) && + (sock->binding[channel].numbound > 0) ) { + address = &sock->binding[channel].address[0]; + } + break; + } + return(address); +} + +/* Send a vector of packets to the the channels specified within the packet. + If the channel specified in the packet is -1, the packet will be sent to + the address in the 'src' member of the packet. + Each packet will be updated with the status of the packet after it has + been sent, -1 if the packet send failed. + This function returns the number of packets sent. +*/ +int SDLNet_UDP_SendV(UDPsocket sock, UDPpacket **packets, int npackets) +{ + int numsent, i, j; + struct UDP_channel *binding; + int status; + int sock_len; + struct sockaddr_in sock_addr; + + if ( sock == NULL ) { + SDLNet_SetError("Passed a NULL socket"); + return(0); + } + + /* Set up the variables to send packets */ + sock_len = sizeof(sock_addr); + + numsent = 0; + for ( i=0; ipacketloss) { + if ((random()%100) <= sock->packetloss) { + packets[i]->status = packets[i]->len; + ++numsent; + continue; + } + } + + /* if channel is < 0, then use channel specified in sock */ + + if ( packets[i]->channel < 0 ) + { + sock_addr.sin_addr.s_addr = packets[i]->address.host; + sock_addr.sin_port = packets[i]->address.port; + sock_addr.sin_family = AF_INET; + status = sendto(sock->channel, + packets[i]->data, packets[i]->len, 0, + (struct sockaddr *)&sock_addr,sock_len); + if ( status >= 0 ) + { + packets[i]->status = status; + ++numsent; + } + } + else + { + /* Send to each of the bound addresses on the channel */ +#ifdef DEBUG_NET + printf("SDLNet_UDP_SendV sending packet to channel = %d\n", packets[i]->channel ); +#endif + + binding = &sock->binding[packets[i]->channel]; + + for ( j=binding->numbound-1; j>=0; --j ) + { + sock_addr.sin_addr.s_addr = binding->address[j].host; + sock_addr.sin_port = binding->address[j].port; + sock_addr.sin_family = AF_INET; + status = sendto(sock->channel, + packets[i]->data, packets[i]->len, 0, + (struct sockaddr *)&sock_addr,sock_len); + if ( status >= 0 ) + { + packets[i]->status = status; + ++numsent; + } + } + } + } + + return(numsent); +} + +int SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet) +{ + /* This is silly, but... */ + packet->channel = channel; + return(SDLNet_UDP_SendV(sock, &packet, 1)); +} + +/* Returns true if a socket is has data available for reading right now */ +static int SocketReady(SOCKET sock) +{ + int retval = 0; + struct timeval tv; + fd_set mask; + + /* Check the file descriptors for available data */ + do { + SDLNet_SetLastError(0); + + /* Set up the mask of file descriptors */ + FD_ZERO(&mask); + FD_SET(sock, &mask); + + /* Set up the timeout */ + tv.tv_sec = 0; + tv.tv_usec = 0; + + /* Look! */ + retval = select(sock+1, &mask, NULL, NULL, &tv); + } while ( SDLNet_GetLastError() == EINTR ); + + return(retval == 1); +} + +/* Receive a vector of pending packets from the UDP socket. + The returned packets contain the source address and the channel they arrived + on. If they did not arrive on a bound channel, the the channel will be set + to -1. + This function returns the number of packets read from the network, or -1 + on error. This function does not block, so can return 0 packets pending. +*/ +extern int SDLNet_UDP_RecvV(UDPsocket sock, UDPpacket **packets) +{ + int numrecv, i, j; + struct UDP_channel *binding; + socklen_t sock_len; + struct sockaddr_in sock_addr; + + if ( sock == NULL ) { + return(0); + } + + numrecv = 0; + while ( packets[numrecv] && SocketReady(sock->channel) ) + { + UDPpacket *packet; + + packet = packets[numrecv]; + + sock_len = sizeof(sock_addr); + packet->status = recvfrom(sock->channel, + packet->data, packet->maxlen, 0, + (struct sockaddr *)&sock_addr, + &sock_len); + if ( packet->status >= 0 ) { + packet->len = packet->status; + packet->address.host = sock_addr.sin_addr.s_addr; + packet->address.port = sock_addr.sin_port; + packet->channel = -1; + + for (i=(SDLNET_MAX_UDPCHANNELS-1); i>=0; --i ) + { + binding = &sock->binding[i]; + + for ( j=binding->numbound-1; j>=0; --j ) + { + if ( (packet->address.host == binding->address[j].host) && + (packet->address.port == binding->address[j].port) ) + { + packet->channel = i; + goto foundit; /* break twice */ + } + } + } +foundit: + ++numrecv; + } + + else + { + packet->len = 0; + } + } + + sock->ready = 0; + + return(numrecv); +} + +/* Receive a single packet from the UDP socket. + The returned packet contains the source address and the channel it arrived + on. If it did not arrive on a bound channel, the the channel will be set + to -1. + This function returns the number of packets read from the network, or -1 + on error. This function does not block, so can return 0 packets pending. +*/ +int SDLNet_UDP_Recv(UDPsocket sock, UDPpacket *packet) +{ + UDPpacket *packets[2]; + + /* Receive a packet array of 1 */ + packets[0] = packet; + packets[1] = NULL; + return(SDLNet_UDP_RecvV(sock, packets)); +} + +/* Close a UDP network socket */ +extern void SDLNet_UDP_Close(UDPsocket sock) +{ + if ( sock != NULL ) + { + if ( sock->channel != INVALID_SOCKET ) + { + closesocket(sock->channel); + } + + free(sock); + } +} + diff --git a/libs/SDL_net/SDLnetselect.c b/libs/SDL_net/SDLnetselect.c new file mode 100644 index 0000000..e14e0c3 --- /dev/null +++ b/libs/SDL_net/SDLnetselect.c @@ -0,0 +1,163 @@ +/* + SDL_net: An example cross-platform network library for use with SDL + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#include "SDLnetsys.h" +#include "SDL_net.h" + +/* The select() API for network sockets */ + +struct SDLNet_Socket { + int ready; + SOCKET channel; +}; + +struct _SDLNet_SocketSet { + int numsockets; + int maxsockets; + struct SDLNet_Socket **sockets; +}; + +/* Allocate a socket set for use with SDLNet_CheckSockets() + This returns a socket set for up to 'maxsockets' sockets, or NULL if + the function ran out of memory. + */ +SDLNet_SocketSet SDLNet_AllocSocketSet(int maxsockets) +{ + struct _SDLNet_SocketSet *set; + int i; + + set = (struct _SDLNet_SocketSet *)malloc(sizeof(*set)); + if ( set != NULL ) { + set->numsockets = 0; + set->maxsockets = maxsockets; + set->sockets = (struct SDLNet_Socket **)malloc + (maxsockets*sizeof(*set->sockets)); + if ( set->sockets != NULL ) { + for ( i=0; isockets[i] = NULL; + } + } else { + free(set); + set = NULL; + } + } + return(set); +} + +/* Add a socket to a set of sockets to be checked for available data */ +int SDLNet_AddSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock) +{ + if ( sock != NULL ) { + if ( set->numsockets == set->maxsockets ) { + SDLNet_SetError("socketset is full"); + return(-1); + } + set->sockets[set->numsockets++] = (struct SDLNet_Socket *)sock; + } + return(set->numsockets); +} + +/* Remove a socket from a set of sockets to be checked for available data */ +int SDLNet_DelSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock) +{ + int i; + + if ( sock != NULL ) { + for ( i=0; inumsockets; ++i ) { + if ( set->sockets[i] == (struct SDLNet_Socket *)sock ) { + break; + } + } + if ( i == set->numsockets ) { + SDLNet_SetError("socket not found in socketset"); + return(-1); + } + --set->numsockets; + for ( ; inumsockets; ++i ) { + set->sockets[i] = set->sockets[i+1]; + } + } + return(set->numsockets); +} + +/* This function checks to see if data is available for reading on the + given set of sockets. If 'timeout' is 0, it performs a quick poll, + otherwise the function returns when either data is available for + reading, or the timeout in milliseconds has elapsed, which ever occurs + first. This function returns the number of sockets ready for reading, + or -1 if there was an error with the select() system call. +*/ +int SDLNet_CheckSockets(SDLNet_SocketSet set, Uint32 timeout) +{ + int i; + SOCKET maxfd; + int retval; + struct timeval tv; + fd_set mask; + + /* Find the largest file descriptor */ + maxfd = 0; + for ( i=set->numsockets-1; i>=0; --i ) { + if ( set->sockets[i]->channel > maxfd ) { + maxfd = set->sockets[i]->channel; + } + } + + /* Check the file descriptors for available data */ + do { + SDLNet_SetLastError(0); + + /* Set up the mask of file descriptors */ + FD_ZERO(&mask); + for ( i=set->numsockets-1; i>=0; --i ) { + FD_SET(set->sockets[i]->channel, &mask); + } + + /* Set up the timeout */ + tv.tv_sec = timeout/1000; + tv.tv_usec = (timeout%1000)*1000; + + /* Look! */ + retval = select(maxfd+1, &mask, NULL, NULL, &tv); + } while ( SDLNet_GetLastError() == EINTR ); + + /* Mark all file descriptors ready that have data available */ + if ( retval > 0 ) { + for ( i=set->numsockets-1; i>=0; --i ) { + if ( FD_ISSET(set->sockets[i]->channel, &mask) ) { + set->sockets[i]->ready = 1; + } + } + } + return(retval); +} + +/* Free a set of sockets allocated by SDL_NetAllocSocketSet() */ +extern void SDLNet_FreeSocketSet(SDLNet_SocketSet set) +{ + if ( set ) { + free(set->sockets); + free(set); + } +} + diff --git a/libs/SDL_net/SDLnetsys.h b/libs/SDL_net/SDLnetsys.h new file mode 100644 index 0000000..b5cd95b --- /dev/null +++ b/libs/SDL_net/SDLnetsys.h @@ -0,0 +1,94 @@ +/* + SDL_net: An example cross-platform network library for use with SDL + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +/* Include normal system headers */ +#include +#include +#include + +#ifndef _WIN32_WCE +#include +#endif + +/* Include system network headers */ +#if defined(__WIN32__) || defined(WIN32) +#define __USE_W32_SOCKETS +#ifdef _WIN64 +#include +#include +#else +#include +/* NOTE: windows socklen_t is signed + * and is defined only for winsock2. */ +typedef int socklen_t; +#endif /* W64 */ +#include +#else /* UNIX */ +#include +#ifdef __FreeBSD__ +#include +#endif +#include +#include +#include +#include +#include +#ifndef __BEOS__ +#include +#endif +#ifdef linux /* FIXME: what other platforms have this? */ +#include +#endif +#include +#include +#include +#endif /* WIN32 */ + +/* FIXME: What platforms need this? */ +#if 0 +typedef Uint32 socklen_t; +#endif + +/* System-dependent definitions */ +#ifndef __USE_W32_SOCKETS +#ifdef __OS2__ +#define closesocket soclose +#else /* !__OS2__ */ +#define closesocket close +#endif /* __OS2__ */ +#define SOCKET int +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#endif /* __USE_W32_SOCKETS */ + +#ifdef __USE_W32_SOCKETS +#define SDLNet_GetLastError WSAGetLastError +#define SDLNet_SetLastError WSASetLastError +#ifndef EINTR +#define EINTR WSAEINTR +#endif +#else +int SDLNet_GetLastError(void); +void SDLNet_SetLastError(int err); +#endif + diff --git a/rsc/graphics/sprites/aniflower.png b/rsc/graphics/sprites/aniflower.png new file mode 100644 index 0000000000000000000000000000000000000000..f0f3696ef038fc338a6566bfc243ac70ad22aa2e GIT binary patch literal 869 zcmV-r1DgDaP)WdH}-0TlrR27DUwf&c&j2XskIMF-je6#)eSE@ZV300009a7bBm000XT z000XT0n*)m`~Uy~<4Ht8RCwC$nK6shU=W5UVr8l5S%_91Dq37;V_{{hmV*9*LBYa8uhiPcZW~e2O1a`r(b9H(@7~*C$d_E=H}hRqHbV&6usrk5yz?cyNi~z1 zj4@ms8d|`z$7sO7F&Z#%j0OxGhx>f#+nv{XeCFAWSOxztJS0Wc`6~zsj-Ebts&z$E*aw+B_|@4NCdWex>jz za;*Ml1MJjqK7A*14@kFJ^9*QomvH8#wb`8_-_8yBIy6oX0`<2VplIOeL1Vs*N)&Dl zw$d#>&t0NGPP^lThwE?t74-SbS3L$%b=PJJU(22&=mQxWuc0LSyKiNK3=TKhUAWn_ za{8MMxc2@&nyWJoW7FlFEnVFu#2ReR+ac&5Z!bTtk#(zeCyyVlzvF*^=8$CkvxDwh zzD?(`HdUUU)b2pWBIs{5K+~i|e+?-us`a(*C^pdASpEC0fYgH$ZIULBqb9VG?;o8& z{oQ}Wa8*6GdNxgP`|3nL{&<-4OqghYRM)FFTaRid@7z#0?Z;bQ-9xAlE}e}on6sU!5)E+M)z4_Mk54H!5^0|t)21RC-Ga$>oK00000NkvXXu0mjf_2`kJ literal 0 HcmV?d00001 diff --git a/rsc/graphics/sprites/elliot2.bmp b/rsc/graphics/sprites/elliot2.bmp new file mode 100644 index 0000000000000000000000000000000000000000..89f66d82713f3ed60315c0f8ff1538aee2a954fc GIT binary patch literal 73782 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 L|0}`xwr%_m)tPtO literal 0 HcmV?d00001 diff --git a/rsc/graphics/tilesets/terrain.bmp b/rsc/graphics/tilesets/terrain.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0a39c74c88f031545124e48e0c2f01249ab064cf GIT binary patch literal 553014 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 b{-SX|w1+m + +int SDL_main(int, char**) { + return 0; +} \ No newline at end of file diff --git a/server/unit.cpp b/server/unit.cpp new file mode 100644 index 0000000..e69de29