complete rewrite
This commit is contained in:
parent
b60e3a0ef6
commit
8a717a2b2e
41 changed files with 2625 additions and 1255 deletions
11
engine/CMakeLists.txt
Normal file
11
engine/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
project(assaultfish)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}")
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
add_subdirectory(external/glad)
|
||||
|
||||
add_subdirectory(net)
|
||||
|
||||
add_subdirectory(tools)
|
||||
3
engine/README.txt
Normal file
3
engine/README.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
GearBox game engine source code
|
||||
|
||||
Licensed under 3-clause BSD
|
||||
14
engine/external/glad/CMakeLists.txt
vendored
Normal file
14
engine/external/glad/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
project(glad)
|
||||
|
||||
add_library(
|
||||
glad
|
||||
STATIC
|
||||
src/glad.c
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
glad
|
||||
PUBLIC
|
||||
include
|
||||
)
|
||||
311
engine/external/glad/include/KHR/khrplatform.h
vendored
Normal file
311
engine/external/glad/include/KHR/khrplatform.h
vendored
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
||||
2653
engine/external/glad/include/glad/glad.h
vendored
Normal file
2653
engine/external/glad/include/glad/glad.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1448
engine/external/glad/src/glad.c
vendored
Normal file
1448
engine/external/glad/src/glad.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
20
engine/net/.clang-format
Normal file
20
engine/net/.clang-format
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
Language: Cpp
|
||||
UseTab: Always
|
||||
TabWidth: 8
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: true
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: true
|
||||
IndentWidth: 8
|
||||
PointerAlignment: Left
|
||||
ColumnLimit: 0
|
||||
AllowShortIfStatementsOnASingleLine: Always
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
SpaceBeforeParens: Never
|
||||
AlignEscapedNewlines: DontAlign
|
||||
SortIncludes: false
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
#IndentPPDirectives: AfterHash
|
||||
21
engine/net/CMakeLists.txt
Normal file
21
engine/net/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
project(libfishsoup)
|
||||
|
||||
add_library(fishsoup SHARED src/client.c src/server.c src/socket.c src/stb_ds.c)
|
||||
|
||||
target_include_directories(fishsoup PUBLIC include)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(ZLIB REQUIRED zlib)
|
||||
target_compile_options(fishsoup PRIVATE ${ZLIB_CFLAGS_OTHER})
|
||||
target_include_directories(fishsoup PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_link_options(fishsoup PRIVATE ${ZLIB_LDFLAGS_OTHER})
|
||||
target_link_directories(fishsoup PRIVATE ${ZLIB_LIBRARY_DIRS})
|
||||
target_link_libraries(fishsoup PRIVATE ${ZLIB_LIBRARIES})
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(
|
||||
TARGETS fishsoup
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
24
engine/net/LICENSE
Normal file
24
engine/net/LICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2025, Fishsoup
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
33
engine/net/Makefile
Normal file
33
engine/net/Makefile
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
CC = cc
|
||||
CFLAGS = -g -Iinclude -fPIC `pkg-config --cflags zlib`
|
||||
LDFLAGS = -shared
|
||||
LIBS = `pkg-config --libs zlib`
|
||||
|
||||
OBJS = src/client.o src/server.o src/socket.o src/stb_ds.o
|
||||
|
||||
LIB = lib
|
||||
|
||||
SO = .so
|
||||
|
||||
.PHONY: all format clean
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
all: $(LIB)fishsoup$(SO)
|
||||
|
||||
format:
|
||||
clang-format --verbose -i `find src include "(" -name "*.c" -or -name "*.h" ")" -and -not -name "stb_*.h"` test*.c
|
||||
|
||||
$(LIB)fishsoup$(SO): $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
testclient: testclient.c $(LIB)fishsoup$(SO)
|
||||
cc -o $@ testclient.c -Wl,-R./ -L./ -Iinclude -lfishsoup
|
||||
|
||||
testserver: testserver.c $(LIB)fishsoup$(SO)
|
||||
cc -o $@ testserver.c -Wl,-R./ -L./ -Iinclude -lfishsoup
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *$(SO) $(OBJS) ./testserver ./testclient
|
||||
86
engine/net/include/fishsoup.h
Normal file
86
engine/net/include/fishsoup.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef __FISHSOUP_H__
|
||||
#define __FISHSOUP_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
|
||||
/* TCP */
|
||||
#define FISHSOUP_PORT 3219
|
||||
|
||||
#define FISHSOUP_PING 3
|
||||
|
||||
enum fishsoup_packet_type {
|
||||
FISHSOUP_PACKET_PING = 0,
|
||||
FISHSOUP_PACKET_PONG,
|
||||
FISHSOUP_PACKET_USER
|
||||
};
|
||||
|
||||
typedef struct fishsoup_client fishsoup_client_t;
|
||||
typedef struct fishsoup_server fishsoup_server_t;
|
||||
typedef struct fishsoup_packet_header fishsoup_packet_header_t;
|
||||
typedef struct fishsoup_packet fishsoup_packet_t;
|
||||
|
||||
struct fishsoup_client {
|
||||
int fd;
|
||||
time_t last_ping;
|
||||
void (*on_packet)(fishsoup_client_t* client, fishsoup_packet_t* packet);
|
||||
void (*on_disconnect)(fishsoup_client_t* client);
|
||||
void* user;
|
||||
struct {
|
||||
int sent_ping;
|
||||
int cleanup;
|
||||
} server;
|
||||
};
|
||||
|
||||
struct fishsoup_server {
|
||||
int fd;
|
||||
struct sockaddr_in address;
|
||||
fishsoup_client_t* clients;
|
||||
void* user;
|
||||
void (*on_client)(fishsoup_server_t* server, fishsoup_client_t* client);
|
||||
void (*on_tick)(fishsoup_server_t* server);
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct fishsoup_packet_header {
|
||||
unsigned char type;
|
||||
unsigned short length; /* ok, seriously, who sends 64k at ONCE */
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
/* !!! REMEMBER TO FREE DATA AFTER RECV UNLESS IT'S NULL !!! */
|
||||
struct fishsoup_packet {
|
||||
fishsoup_packet_header_t header;
|
||||
void* data;
|
||||
};
|
||||
|
||||
fishsoup_client_t* fishsoup_client(const char* host, int port);
|
||||
void fishsoup_client_on_packet(fishsoup_client_t* client, void (*on_packet)(fishsoup_client_t* client, fishsoup_packet_t* packet));
|
||||
void fishsoup_client_on_disconnect(fishsoup_client_t* client, void (*on_disconnect)(fishsoup_client_t* client));
|
||||
int fishsoup_client_poll(fishsoup_client_t* client);
|
||||
void fishsoup_client_destroy(fishsoup_client_t* client);
|
||||
|
||||
fishsoup_server_t* fishsoup_server(int port);
|
||||
int fishsoup_server_clients(fishsoup_server_t* server);
|
||||
void fishsoup_server_loop(fishsoup_server_t* server);
|
||||
void fishsoup_server_on_client(fishsoup_server_t* server, void (*on_client)(fishsoup_server_t* server, fishsoup_client_t* client));
|
||||
void fishsoup_server_on_tick(fishsoup_server_t* server, void (*on_tick)(fishsoup_server_t* server));
|
||||
void fishsoup_server_broadcast(fishsoup_server_t* server, fishsoup_packet_t* packet);
|
||||
void fishsoup_server_destroy(fishsoup_server_t* server);
|
||||
|
||||
int fishsoup_fd_send(int fd, const void* buf, int length);
|
||||
int fishsoup_fd_recv(int fd, void* buf, int length, int to);
|
||||
int fishsoup_packet_send(int fd, fishsoup_packet_t* packet);
|
||||
int fishsoup_packet_recv(int fd, fishsoup_packet_t* packet);
|
||||
|
||||
#endif
|
||||
93
engine/net/src/client.c
Normal file
93
engine/net/src/client.c
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#include <fishsoup.h>
|
||||
|
||||
fishsoup_client_t* fishsoup_client(const char* host, int port) {
|
||||
fishsoup_client_t* client = malloc(sizeof(*client));
|
||||
struct addrinfo hints;
|
||||
char p[32];
|
||||
struct addrinfo* result;
|
||||
struct addrinfo* rp;
|
||||
|
||||
if(port == 0) port = FISHSOUP_PORT;
|
||||
sprintf(p, "%d", port);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if(getaddrinfo(host, p, &hints, &result) != 0) {
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
int val = 1;
|
||||
|
||||
if((client->fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
val = 1;
|
||||
setsockopt(client->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
|
||||
|
||||
if(connect(client->fd, rp->ai_addr, rp->ai_addrlen) >= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
close(client->fd);
|
||||
}
|
||||
|
||||
if(rp == NULL) {
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
client->on_packet = NULL;
|
||||
client->on_disconnect = NULL;
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void fishsoup_client_on_packet(fishsoup_client_t* client, void (*on_packet)(fishsoup_client_t* client, fishsoup_packet_t* packet)) {
|
||||
client->on_packet = on_packet;
|
||||
}
|
||||
|
||||
void fishsoup_client_on_disconnect(fishsoup_client_t* client, void (*on_disconnect)(fishsoup_client_t* client)) {
|
||||
client->on_disconnect = on_disconnect;
|
||||
}
|
||||
|
||||
int fishsoup_client_poll(fishsoup_client_t* client) {
|
||||
struct pollfd pfd;
|
||||
fishsoup_packet_t pkt;
|
||||
int ret;
|
||||
|
||||
pfd.fd = client->fd;
|
||||
pfd.events = POLLIN | POLLPRI;
|
||||
ret = poll(&pfd, 1, 0);
|
||||
if(ret <= 0) return ret;
|
||||
|
||||
ret = fishsoup_packet_recv(client->fd, &pkt);
|
||||
if(ret == 0) return -1; /* connection broke, probably */
|
||||
|
||||
if(pkt.header.type == FISHSOUP_PACKET_PING) {
|
||||
fishsoup_packet_t ret;
|
||||
client->last_ping = time(NULL);
|
||||
|
||||
ret.header.type = FISHSOUP_PACKET_PONG;
|
||||
ret.header.length = 0;
|
||||
ret.data = NULL;
|
||||
if(fishsoup_packet_send(client->fd, &ret) == 0) {
|
||||
if(pkt.data != NULL) free(pkt.data);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(client->on_packet != NULL) client->on_packet(client, &pkt);
|
||||
|
||||
if(pkt.data != NULL) free(pkt.data);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void fishsoup_client_destroy(fishsoup_client_t* client) {
|
||||
if(client->on_disconnect != NULL) client->on_disconnect(client);
|
||||
close(client->fd);
|
||||
free(client);
|
||||
}
|
||||
199
engine/net/src/server.c
Normal file
199
engine/net/src/server.c
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
#include <fishsoup.h>
|
||||
|
||||
#include "stb_ds.h"
|
||||
|
||||
fishsoup_server_t* fishsoup_server(int port) {
|
||||
fishsoup_server_t* server = malloc(sizeof(*server));
|
||||
int val;
|
||||
|
||||
if(port == 0) port = FISHSOUP_PORT;
|
||||
|
||||
if((server->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
|
||||
free(server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
val = 1;
|
||||
setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
|
||||
|
||||
server->address.sin_family = AF_INET;
|
||||
server->address.sin_addr.s_addr = INADDR_ANY;
|
||||
server->address.sin_port = htons(port);
|
||||
|
||||
if(bind(server->fd, (struct sockaddr*)&server->address, sizeof(server->address)) < 0) {
|
||||
close(server->fd);
|
||||
free(server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(listen(server->fd, 128) < 0) {
|
||||
close(server->fd);
|
||||
free(server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
server->clients = NULL;
|
||||
server->on_client = NULL;
|
||||
server->on_tick = NULL;
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
int fishsoup_server_clients(fishsoup_server_t* server) {
|
||||
return arrlen(server->clients);
|
||||
}
|
||||
|
||||
void fishsoup_server_loop(fishsoup_server_t* server) {
|
||||
struct pollfd* pfd;
|
||||
|
||||
while(1) {
|
||||
int i;
|
||||
int ret;
|
||||
pfd = malloc(sizeof(*pfd) * (1 + arrlen(server->clients)));
|
||||
|
||||
pfd[0].fd = server->fd;
|
||||
pfd[0].events = POLLIN | POLLPRI;
|
||||
|
||||
for(i = 0; i < arrlen(server->clients); i++) {
|
||||
pfd[1 + i].fd = server->clients[i].fd;
|
||||
pfd[1 + i].events = POLLIN | POLLPRI;
|
||||
}
|
||||
|
||||
ret = poll(pfd, 1 + arrlen(server->clients), 50);
|
||||
if(ret < 0) {
|
||||
free(pfd);
|
||||
break;
|
||||
}
|
||||
|
||||
if(server->on_tick != NULL) server->on_tick(server);
|
||||
|
||||
if(pfd[0].revents & POLLIN) {
|
||||
fishsoup_client_t client;
|
||||
struct sockaddr_in address;
|
||||
int len = sizeof(address);
|
||||
|
||||
client.fd = accept(server->fd, (struct sockaddr*)&address, &len);
|
||||
client.last_ping = time(NULL);
|
||||
client.server.cleanup = 0;
|
||||
client.server.sent_ping = 0;
|
||||
client.on_packet = NULL;
|
||||
|
||||
arrput(server->clients, client);
|
||||
|
||||
if(server->on_client != NULL) server->on_client(server, &server->clients[arrlen(server->clients) - 1]);
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Client %d accepted\n", server->clients[i].fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
for(i = 0; i < arrlen(server->clients); i++) {
|
||||
time_t diff;
|
||||
if(server->clients[i].server.cleanup) continue;
|
||||
if(pfd[1 + i].revents & POLLIN) {
|
||||
fishsoup_packet_t pkt;
|
||||
|
||||
if(fishsoup_packet_recv(server->clients[i].fd, &pkt) == 0) {
|
||||
server->clients[i].server.cleanup = 1;
|
||||
} else {
|
||||
if(pkt.header.type == FISHSOUP_PACKET_PONG && server->clients[i].server.sent_ping) {
|
||||
server->clients[i].last_ping = time(NULL);
|
||||
server->clients[i].server.sent_ping = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Pong from client %d\n", server->clients[i].fd);
|
||||
#endif
|
||||
} else if(pkt.header.type == FISHSOUP_PACKET_PING) {
|
||||
fishsoup_packet_t ret;
|
||||
|
||||
ret.header.type = FISHSOUP_PACKET_PONG;
|
||||
ret.header.length = 0;
|
||||
ret.data = NULL;
|
||||
|
||||
if(fishsoup_packet_send(server->clients[i].fd, &ret) == 0) {
|
||||
server->clients[i].server.cleanup = 1;
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Ping from client %d, sending back pong\n", server->clients[i].fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(!server->clients[i].server.cleanup && server->clients[i].on_packet != NULL) server->clients[i].on_packet(&server->clients[i], &pkt);
|
||||
|
||||
if(pkt.data != NULL) free(pkt.data);
|
||||
}
|
||||
}
|
||||
if(server->clients[i].server.cleanup) continue;
|
||||
|
||||
diff = time(NULL) - server->clients[i].last_ping;
|
||||
|
||||
if(diff == FISHSOUP_PING && !server->clients[i].server.sent_ping) {
|
||||
fishsoup_packet_t pkt;
|
||||
|
||||
pkt.header.type = FISHSOUP_PACKET_PING;
|
||||
pkt.header.length = 0;
|
||||
pkt.data = NULL;
|
||||
|
||||
if(fishsoup_packet_send(server->clients[i].fd, &pkt) == 0) {
|
||||
server->clients[i].server.cleanup = 1;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Failed to ping client %d\n", server->clients[i].fd);
|
||||
#endif
|
||||
} else {
|
||||
server->clients[i].server.sent_ping = 1;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Pinging client %d\n", server->clients[i].fd);
|
||||
#endif
|
||||
}
|
||||
} else if(diff == FISHSOUP_PING * 2 && server->clients[i].server.sent_ping) {
|
||||
server->clients[i].server.cleanup = 1;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Ping timeout (client %d)\n", server->clients[i].fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < arrlen(server->clients); i++) {
|
||||
if(server->clients[i].server.cleanup) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Client %d cleanup\n", server->clients[i].fd);
|
||||
#endif
|
||||
if(server->clients[i].on_disconnect != NULL) server->clients[i].on_disconnect(&server->clients[i]);
|
||||
close(server->clients[i].fd);
|
||||
arrdel(server->clients, i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
free(pfd);
|
||||
}
|
||||
}
|
||||
|
||||
void fishsoup_server_on_client(fishsoup_server_t* server, void (*on_client)(fishsoup_server_t* server, fishsoup_client_t* client)) {
|
||||
server->on_client = on_client;
|
||||
}
|
||||
|
||||
void fishsoup_server_on_tick(fishsoup_server_t* server, void (*on_tick)(fishsoup_server_t* server)) {
|
||||
server->on_tick = on_tick;
|
||||
}
|
||||
|
||||
void fishsoup_server_destroy(fishsoup_server_t* server) {
|
||||
int i;
|
||||
for(i = 0; i < arrlen(server->clients); i++) {
|
||||
if(server->clients[i].on_disconnect != NULL) server->clients[i].on_disconnect(&server->clients[i]);
|
||||
close(server->clients[i].fd);
|
||||
}
|
||||
arrfree(server->clients);
|
||||
close(server->fd);
|
||||
free(server);
|
||||
}
|
||||
|
||||
void fishsoup_server_broadcast(fishsoup_server_t* server, fishsoup_packet_t* packet) {
|
||||
int i;
|
||||
for(i = 0; i < arrlen(server->clients); i++) {
|
||||
if(fishsoup_packet_send(server->clients[i].fd, packet) == 0) {
|
||||
server->clients[i].server.cleanup = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
200
engine/net/src/socket.c
Normal file
200
engine/net/src/socket.c
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
#include <fishsoup.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#define CHUNK 512
|
||||
|
||||
int fishsoup_packet_send(int fd, fishsoup_packet_t* packet) {
|
||||
int r;
|
||||
fishsoup_packet_header_t h;
|
||||
int len = 0;
|
||||
unsigned char* data = NULL;
|
||||
|
||||
if(packet->header.length > 0) {
|
||||
z_stream strm;
|
||||
int in = packet->header.length;
|
||||
int seek = 0;
|
||||
int flush = 0;
|
||||
unsigned char buf[CHUNK];
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
if(deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) return 0;
|
||||
|
||||
do {
|
||||
strm.avail_in = in <= CHUNK ? in : CHUNK;
|
||||
strm.next_in = ((unsigned char*)packet->data) + seek;
|
||||
|
||||
flush = in <= CHUNK ? Z_FINISH : Z_NO_FLUSH;
|
||||
|
||||
do {
|
||||
int have;
|
||||
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = buf;
|
||||
|
||||
deflate(&strm, flush);
|
||||
|
||||
have = CHUNK - strm.avail_out;
|
||||
|
||||
if(data == NULL) {
|
||||
data = malloc(have);
|
||||
memcpy(data, buf, have);
|
||||
} else {
|
||||
unsigned char* old = data;
|
||||
|
||||
data = malloc(len + have);
|
||||
memcpy(data, old, len);
|
||||
memcpy(data + len, buf, have);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
len += have;
|
||||
} while(strm.avail_out == 0);
|
||||
|
||||
seek += CHUNK;
|
||||
} while(flush != Z_FINISH);
|
||||
|
||||
deflateEnd(&strm);
|
||||
}
|
||||
|
||||
memcpy(&h, &packet->header, sizeof(h));
|
||||
h.length = htons(len);
|
||||
|
||||
r = fishsoup_fd_send(fd, &h, sizeof(h));
|
||||
if(r != sizeof(h)) return 0;
|
||||
|
||||
if(data != NULL) {
|
||||
r = fishsoup_fd_send(fd, data, len);
|
||||
if(r != sizeof(h)) {
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
return sizeof(h) + len;
|
||||
}
|
||||
|
||||
int fishsoup_packet_recv(int fd, fishsoup_packet_t* packet) {
|
||||
fishsoup_packet_header_t h;
|
||||
|
||||
if(fishsoup_fd_recv(fd, &h, sizeof(h), 500) != sizeof(h)) {
|
||||
packet->data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&packet->header, &h, sizeof(h));
|
||||
packet->header.length = ntohs(packet->header.length);
|
||||
packet->data = NULL;
|
||||
if(packet->header.length > 0) {
|
||||
unsigned char* data = malloc(packet->header.length);
|
||||
z_stream strm;
|
||||
int in = packet->header.length;
|
||||
int ret;
|
||||
int seek = 0;
|
||||
int len = 0;
|
||||
unsigned char buf[CHUNK];
|
||||
if(fishsoup_fd_recv(fd, data, packet->header.length, 50) != packet->header.length) {
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = 0;
|
||||
strm.next_in = Z_NULL;
|
||||
|
||||
if(inflateInit(&strm) != Z_OK) {
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
strm.avail_in = in <= CHUNK ? in : CHUNK;
|
||||
strm.next_in = data + seek;
|
||||
|
||||
do {
|
||||
int have;
|
||||
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = buf;
|
||||
|
||||
ret = inflate(&strm, Z_NO_FLUSH);
|
||||
|
||||
if(ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
|
||||
inflateEnd(&strm);
|
||||
if(packet->data != NULL) {
|
||||
free(packet->data);
|
||||
packet->data = NULL;
|
||||
}
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
have = CHUNK - strm.avail_out;
|
||||
|
||||
if(packet->data == NULL) {
|
||||
packet->data = malloc(have);
|
||||
memcpy(packet->data, buf, have);
|
||||
} else {
|
||||
unsigned char* old = packet->data;
|
||||
|
||||
packet->data = malloc(len + have);
|
||||
memcpy(packet->data, old, len);
|
||||
memcpy(packet->data + len, buf, have);
|
||||
|
||||
free(packet->data);
|
||||
}
|
||||
|
||||
len += have;
|
||||
} while(strm.avail_out == 0);
|
||||
} while(ret != Z_STREAM_END);
|
||||
|
||||
inflateEnd(&strm);
|
||||
|
||||
free(data);
|
||||
|
||||
packet->header.length = len;
|
||||
}
|
||||
|
||||
return sizeof(h) + packet->header.length;
|
||||
}
|
||||
|
||||
int fishsoup_fd_send(int fd, const void* buf, int length) {
|
||||
struct pollfd pfd;
|
||||
pfd.fd = fd;
|
||||
pfd.events = POLLOUT | POLLPRI;
|
||||
|
||||
poll(&pfd, 1, 0);
|
||||
if(!(pfd.revents & POLLOUT)) return 0;
|
||||
|
||||
return send(fd, buf, length, 0);
|
||||
}
|
||||
|
||||
int fishsoup_fd_recv(int fd, void* buf, int length, int to) {
|
||||
struct pollfd pfd;
|
||||
int ret;
|
||||
unsigned char* b = buf;
|
||||
int i = 0;
|
||||
time_t t = time(NULL);
|
||||
|
||||
pfd.fd = fd;
|
||||
pfd.events = POLLIN | POLLPRI;
|
||||
|
||||
while(i < length) {
|
||||
/* i think 500 is reasonable timeout. no? */
|
||||
ret = poll(&pfd, 1, to);
|
||||
|
||||
if((time(NULL) - t) >= 5) return i; /* we waited enough! kill the connection */
|
||||
if(ret <= 0) return i;
|
||||
|
||||
ret = recv(fd, &b[i], 1, 0);
|
||||
if(ret < 1) return i;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
2
engine/net/src/stb_ds.c
Normal file
2
engine/net/src/stb_ds.c
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#define STB_DS_IMPLEMENTATION
|
||||
#include "stb_ds.h"
|
||||
1895
engine/net/src/stb_ds.h
Normal file
1895
engine/net/src/stb_ds.h
Normal file
File diff suppressed because it is too large
Load diff
11
engine/net/testclient.c
Normal file
11
engine/net/testclient.c
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include <fishsoup.h>
|
||||
|
||||
void on(fishsoup_client_t* client, fishsoup_packet_t* pkt) {
|
||||
if(pkt->header.type == FISHSOUP_PACKET_USER) printf("Received: %s\n", pkt->data);
|
||||
}
|
||||
|
||||
int main() {
|
||||
fishsoup_client_t* client = fishsoup_client("127.0.0.1", 0);
|
||||
fishsoup_client_on_packet(client, on);
|
||||
while(fishsoup_client_poll(client) >= 0);
|
||||
}
|
||||
18
engine/net/testserver.c
Normal file
18
engine/net/testserver.c
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#include <fishsoup.h>
|
||||
|
||||
void on(fishsoup_server_t* server, fishsoup_client_t* client) {
|
||||
fishsoup_packet_t pkt;
|
||||
|
||||
pkt.data = "Hello world!";
|
||||
pkt.header.length = strlen(pkt.data) + 1;
|
||||
pkt.header.flag = 0;
|
||||
pkt.header.type = FISHSOUP_PACKET_USER;
|
||||
|
||||
fishsoup_packet_send(client->fd, &pkt);
|
||||
}
|
||||
|
||||
int main() {
|
||||
fishsoup_server_t* server = fishsoup_server(0);
|
||||
fishsoup_server_on_client(server, on);
|
||||
fishsoup_server_loop(server);
|
||||
}
|
||||
9
engine/tools/CMakeLists.txt
Normal file
9
engine/tools/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
include(GNUInstallDirs)
|
||||
|
||||
macro(compile_tool name)
|
||||
file(GLOB ${name}_SRC ${name}/*.c)
|
||||
add_executable(${name} ${${name}_SRC})
|
||||
target_include_directories(${name} PRIVATE ../../external/stb)
|
||||
endmacro()
|
||||
|
||||
compile_tool(gb_dumpfont)
|
||||
72
engine/tools/gb_dumpfont/gb_dumpfont.c
Normal file
72
engine/tools/gb_dumpfont/gb_dumpfont.c
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stb_image_write.h>
|
||||
|
||||
#define CHECK(s) ((linelen > strlen(s)) && memcmp(line, s, strlen(s)) == 0)
|
||||
|
||||
int main() {
|
||||
char* line = NULL;
|
||||
size_t linesize;
|
||||
ssize_t linelen;
|
||||
int iw, ih;
|
||||
unsigned char* font = NULL;
|
||||
int c;
|
||||
int p = 0;
|
||||
int y = 0;
|
||||
int w, h;
|
||||
|
||||
while((linelen = getline(&line, &linesize, stdin)) != -1) {
|
||||
linelen--;
|
||||
|
||||
line[linelen] = 0;
|
||||
|
||||
if(CHECK("FONTBOUNDINGBOX ")) {
|
||||
sscanf(line, "FONTBOUNDINGBOX %d %d", &w, &h);
|
||||
printf("%dx%d font\n", w, h);
|
||||
|
||||
if(font != NULL) free(font);
|
||||
|
||||
iw = w * 16;
|
||||
ih = h * 16;
|
||||
font = malloc(iw * ih * 4);
|
||||
memset(font, 0, iw * ih * 4);
|
||||
} else if(CHECK("ENCODING ")) {
|
||||
sscanf(line, "ENCODING %d", &c);
|
||||
} else if(strcmp(line, "BITMAP") == 0) {
|
||||
p = 1;
|
||||
y = 0;
|
||||
} else if(strcmp(line, "ENDCHAR") == 0) {
|
||||
p = 0;
|
||||
} else if(p && y < h) {
|
||||
int bx = c % 16;
|
||||
int by = c / 16;
|
||||
int i;
|
||||
int n = 0;
|
||||
|
||||
for(i = 0; i < w; i++) {
|
||||
if((i % 4) == 0) {
|
||||
char buf[2];
|
||||
|
||||
buf[0] = line[i / 4];
|
||||
buf[1] = 0;
|
||||
|
||||
n = strtol(buf, NULL, 16);
|
||||
}
|
||||
|
||||
if(n & (1 << 3)) {
|
||||
unsigned char* px = &font[((by * h + y) * iw + bx * w + i) * 4];
|
||||
memset(px, 255, 4);
|
||||
}
|
||||
|
||||
n = n << 1;
|
||||
}
|
||||
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
if(font != NULL) stbi_write_png("font.png", iw, ih, 4, font, 0);
|
||||
return 0;
|
||||
}
|
||||
2
engine/tools/gb_dumpfont/stb_image_write.c
Normal file
2
engine/tools/gb_dumpfont/stb_image_write.c
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb_image_write.h>
|
||||
Loading…
Add table
Add a link
Reference in a new issue