complete rewrite
This commit is contained in:
parent
b60e3a0ef6
commit
8a717a2b2e
41 changed files with 2625 additions and 1255 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,6 +1,3 @@
|
|||
[submodule "external/libfishsoup"]
|
||||
path = external/libfishsoup
|
||||
url = ../libfishsoup
|
||||
[submodule "external/milsko"]
|
||||
path = external/milsko
|
||||
url = https://gitea.nishi.boats/pyrite-dev/milsko
|
||||
|
|
|
|||
|
|
@ -5,47 +5,9 @@ include(GNUInstallDirs)
|
|||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}")
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
||||
add_subdirectory(external/libfishsoup)
|
||||
add_subdirectory(external/milsko)
|
||||
add_subdirectory(external/glad)
|
||||
|
||||
file(GLOB SERVER_SRC server/*.c src/*.c)
|
||||
file(GLOB CLIENT_SRC client/*.c src/*.c)
|
||||
|
||||
add_executable(af_server ${SERVER_SRC} external/stb/stb_ds.c)
|
||||
add_executable(af_client ${CLIENT_SRC} external/stb/stb_image.c)
|
||||
add_executable(af_dumpfont tools/af_dumpfont.c external/stb/stb_image_write.c)
|
||||
|
||||
target_include_directories(af_server PRIVATE include external/stb ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_include_directories(af_client PRIVATE include external/stb ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_include_directories(af_dumpfont PRIVATE include external/stb ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_compile_definitions(af_server PRIVATE AF_SERVER)
|
||||
target_compile_definitions(af_client PRIVATE AF_CLIENT)
|
||||
|
||||
target_link_libraries(af_server PRIVATE fishsoup)
|
||||
target_link_libraries(af_client PRIVATE fishsoup Mw glad)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
target_link_libraries(af_client PRIVATE glu32)
|
||||
else()
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(GLU REQUIRED glu)
|
||||
target_compile_options(af_client PRIVATE ${GLU_CFLAGS_OTHER})
|
||||
target_include_directories(af_client PRIVATE ${GLU_INCLUDE_DIRS})
|
||||
target_link_options(af_client PRIVATE ${GLU_LDFLAGS_OTHER})
|
||||
target_link_directories(af_client PRIVATE ${GLU_LIBRARY_DIRS})
|
||||
target_link_libraries(af_client PRIVATE ${GLU_LIBRARIES} m)
|
||||
endif()
|
||||
|
||||
configure_file(include/af_config.h.in af_config.h)
|
||||
|
||||
install(
|
||||
TARGETS af_server af_client af_dumpfont
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
add_subdirectory(engine)
|
||||
|
||||
install(
|
||||
FILES resource/logo.png resource/font.png resource/shadow.vs resource/shadow.fs
|
||||
|
|
|
|||
298
client/gl.c
298
client/gl.c
|
|
@ -1,298 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
#define SHADOW_WIDTH 1024
|
||||
#define SHADOW_HEIGHT 1024
|
||||
|
||||
double gl_cam_lr = 0;
|
||||
double gl_cam_ud = 0;
|
||||
double gl_cam_x;
|
||||
double gl_cam_y;
|
||||
double gl_cam_z;
|
||||
|
||||
int gl_scene = AF_SCENE_MAIN;
|
||||
|
||||
GLuint gl_shadow;
|
||||
|
||||
static GLuint shadow_texture;
|
||||
|
||||
gl_scene_t gl_scenes[] = {
|
||||
{gl_main_changed, gl_main_init, gl_main_draw, gl_main_after}, /**/
|
||||
{NULL, NULL, NULL, NULL} /**/
|
||||
};
|
||||
|
||||
static char gl_status[256];
|
||||
|
||||
void gl_resize(int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
void gl_init(void) {
|
||||
GLfloat lightamb[] = {0.25, 0.25, 0.25, 1.0};
|
||||
GLfloat lightcol[] = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_NORMALIZE);
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
glEnable(GL_LIGHT0);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, lightamb);
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightcol);
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, lightcol);
|
||||
|
||||
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
|
||||
|
||||
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
gl_font_init();
|
||||
|
||||
gl_status[0] = 0;
|
||||
|
||||
glGenTextures(1, &shadow_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, shadow_texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if(gl_shader(&gl_shadow, DATAROOTDIR "/assaultfish/shadow.vs", DATAROOTDIR "/assaultfish/shadow.fs")) {
|
||||
glUseProgram(gl_shadow);
|
||||
glUniform1i(glGetUniformLocation(gl_shadow, "texture"), 1);
|
||||
glUniform1i(glGetUniformLocation(gl_shadow, "texture_in"), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void gl_cube(double x, double y, double z, double xs, double ys, double zs, double xr, double yr, double zr) {
|
||||
int i;
|
||||
glPushMatrix();
|
||||
glTranslatef(x, y, z);
|
||||
glScalef(xs, ys, zs);
|
||||
glRotatef(xr, 1, 0, 0);
|
||||
glRotatef(yr, 0, 1, 0);
|
||||
glRotatef(zr, 0, 0, 1);
|
||||
for(i = 0; i < 6; i++) {
|
||||
glBegin(GL_QUADS);
|
||||
glNormal3f(0, 0, 1);
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex3f(-0.5, 0.5, 0.5);
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex3f(-0.5, -0.5, 0.5);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex3f(0.5, -0.5, 0.5);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex3f(0.5, 0.5, 0.5);
|
||||
glEnd();
|
||||
|
||||
if(i == 3) {
|
||||
glRotatef(90, 1, 0, 0);
|
||||
} else if(i == 4) {
|
||||
glRotatef(180, 1, 0, 0);
|
||||
} else {
|
||||
glRotatef(90, 0, 1, 0);
|
||||
}
|
||||
}
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
static void gl_common(void) {
|
||||
char buf1[128];
|
||||
char buf2[128];
|
||||
int l;
|
||||
gl_util_begin_2d();
|
||||
|
||||
glColor4f(0, 0, 0, 0.5);
|
||||
sprintf(buf1, "AssaultFish %s", AF_VERSION);
|
||||
sprintf(buf2, "%s", AF_COPYRIGHT);
|
||||
|
||||
l = gl_font_width(buf1);
|
||||
if(l < gl_font_width(buf2)) l = gl_font_width(buf2);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(MwGetInteger(opengl, MwNwidth) - l, MwGetInteger(opengl, MwNheight) - gl_font_height(buf1) - gl_font_height(buf2));
|
||||
glVertex2f(MwGetInteger(opengl, MwNwidth) - l, MwGetInteger(opengl, MwNheight));
|
||||
glVertex2f(MwGetInteger(opengl, MwNwidth), MwGetInteger(opengl, MwNheight));
|
||||
glVertex2f(MwGetInteger(opengl, MwNwidth), MwGetInteger(opengl, MwNheight) - gl_font_height(buf1) - gl_font_height(buf2));
|
||||
glEnd();
|
||||
|
||||
glColor3f(1, 1, 1);
|
||||
|
||||
gl_font_text(buf1, MwGetInteger(opengl, MwNwidth) - gl_font_width(buf1), MwGetInteger(opengl, MwNheight) - gl_font_height(buf1) - gl_font_height(buf2), 1);
|
||||
|
||||
gl_font_text(buf2, MwGetInteger(opengl, MwNwidth) - gl_font_width(buf2), MwGetInteger(opengl, MwNheight) - gl_font_height(buf2), 1);
|
||||
|
||||
gl_font_text(gl_status, 0, 0, 1);
|
||||
|
||||
gl_util_end_2d();
|
||||
}
|
||||
|
||||
static void scene(void) {
|
||||
if(gl_scenes[gl_scene].draw != NULL) gl_scenes[gl_scene].draw();
|
||||
}
|
||||
|
||||
void gl_render(void) {
|
||||
GLfloat lightpos[4] = {2.5, 5, -2.5, 1};
|
||||
GLint viewport[4];
|
||||
GLdouble modelview[16];
|
||||
GLdouble modelview2[16];
|
||||
|
||||
if(gl_scenes[gl_scene].init != NULL) gl_scenes[gl_scene].init();
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glGetIntegerv(GL_VIEWPORT, &viewport[0]);
|
||||
|
||||
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
gluPerspective(60, (double)SHADOW_WIDTH / SHADOW_HEIGHT, 0.01, 100.0);
|
||||
gluLookAt(lightpos[0], lightpos[1], lightpos[2], 0, 0, 0, 0, 1, 0);
|
||||
|
||||
glGetDoublev(GL_MODELVIEW_MATRIX, &modelview[0]);
|
||||
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glCullFace(GL_FRONT);
|
||||
|
||||
scene();
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, shadow_texture);
|
||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glEnable(GL_LIGHTING);
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluPerspective(60, (double)MwGetInteger(opengl, MwNwidth) / MwGetInteger(opengl, MwNheight), 0.01, 100.0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
gluLookAt(gl_cam_x, gl_cam_y, gl_cam_z, gl_cam_x + cos(gl_cam_lr / 180.0 * M_PI), gl_cam_y + sin(gl_cam_ud / 180.0 * M_PI), gl_cam_z + sin(gl_cam_lr / 180.0 * M_PI), 0, 1, 0);
|
||||
glGetDoublev(GL_MODELVIEW_MATRIX, &modelview2[0]);
|
||||
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glLoadIdentity();
|
||||
|
||||
glTranslated(0.5, 0.5, 0.5);
|
||||
glScaled(0.5, 0.5, 0.5);
|
||||
glMultMatrixd(modelview);
|
||||
glMultMatrixd(modelview2);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, shadow_texture);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
|
||||
|
||||
scene();
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glLoadIdentity();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
gl_common();
|
||||
|
||||
if(gl_scenes[gl_scene].after != NULL) gl_scenes[gl_scene].after();
|
||||
}
|
||||
|
||||
void gl_scene_change(void) {
|
||||
if(gl_scenes[gl_scene].changed != NULL) gl_scenes[gl_scene].changed();
|
||||
|
||||
gl_status[0] = 0;
|
||||
}
|
||||
|
||||
void gl_set_status(const char* status) {
|
||||
strcpy(gl_status, status);
|
||||
}
|
||||
|
||||
static void cross(double* out, double v00, double v01, double v02, double v10, double v11, double v12) {
|
||||
out[0] = v01 * v12 - v02 * v11;
|
||||
out[1] = v02 * v10 - v00 * v12;
|
||||
out[2] = v00 * v11 - v01 * v10;
|
||||
}
|
||||
|
||||
/*
|
||||
* v0-v2
|
||||
* | /
|
||||
* v1/
|
||||
*/
|
||||
void gl_calc_normal3(double* out, double v00, double v01, double v02, double v10, double v11, double v12, double v20, double v21, double v22) {
|
||||
double v[3];
|
||||
double l = 0;
|
||||
int i;
|
||||
|
||||
cross(&v[0], /**/
|
||||
v10 - v00, v11 - v01, v12 - v02, /**/
|
||||
v20 - v00, v21 - v01, v22 - v02 /**/
|
||||
);
|
||||
|
||||
l = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
out[i] = v[i] / l;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* v0-v3
|
||||
* | |
|
||||
* v1-v2
|
||||
*/
|
||||
void gl_calc_normal4(double* out, double v00, double v01, double v02, double v10, double v11, double v12, double v20, double v21, double v22, double v30, double v31, double v32) {
|
||||
double v0[3];
|
||||
double v1[3];
|
||||
int i;
|
||||
double l = 0;
|
||||
|
||||
gl_calc_normal3(&v0[0], v00, v01, v02, v10, v11, v12, v30, v31, v32);
|
||||
gl_calc_normal3(&v1[0], v10, v11, v12, v20, v21, v22, v30, v31, v32);
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
out[i] = v0[i] + v1[i];
|
||||
}
|
||||
|
||||
l = sqrt(out[0] * out[0] + out[1] * out[1] + out[2] * out[2]);
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
out[i] = out[i] / l;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
#include <stb_image.h>
|
||||
|
||||
static GLuint font_atlas;
|
||||
static int font_width = 0, font_height = 0;
|
||||
|
||||
void gl_font_init(void) {
|
||||
unsigned int ch;
|
||||
unsigned char* data = stbi_load(DATAROOTDIR "/assaultfish/font.png", &font_width, &font_height, &ch, 4);
|
||||
|
||||
glGenTextures(1, &font_atlas);
|
||||
glBindTexture(GL_TEXTURE_2D, font_atlas);
|
||||
if(data != NULL) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, font_width, font_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
free(data);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void gl_font_text(const char* text, int x, int y, double scale) {
|
||||
int i;
|
||||
int gw = font_width / 16 * scale, gh = font_height / 16 * scale;
|
||||
int is = glIsEnabled(GL_TEXTURE_2D) ? 1 : 0;
|
||||
|
||||
gl_util_begin_2d();
|
||||
|
||||
if(!is) glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, font_atlas);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
for(i = 0; text[i] != 0; i++) {
|
||||
double bx = 1.0 / 16 * (text[i] % 16), by = 1.0 / 16 * (text[i] / 16);
|
||||
|
||||
glTexCoord2f(bx, by);
|
||||
glVertex2f(i * gw + x, y);
|
||||
glTexCoord2f(bx + 1.0 / 16, by);
|
||||
glVertex2f(i * gw + x + gw, y);
|
||||
glTexCoord2f(bx + 1.0 / 16, by + 1.0 / 16);
|
||||
glVertex2f(i * gw + x + gw, y + gh);
|
||||
glTexCoord2f(bx, by + 1.0 / 16);
|
||||
glVertex2f(i * gw + x, y + gh);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
if(!is) glDisable(GL_TEXTURE_2D);
|
||||
|
||||
gl_util_end_2d();
|
||||
}
|
||||
|
||||
int gl_font_width(const char* text) {
|
||||
return font_width / 16 * strlen(text);
|
||||
}
|
||||
|
||||
int gl_font_height(const char* text) {
|
||||
return font_height / 16;
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
#include <stb_image.h>
|
||||
|
||||
static double rad;
|
||||
|
||||
void gl_main_changed(void) {
|
||||
rad = 0;
|
||||
}
|
||||
|
||||
void gl_main_init(void) {
|
||||
gl_cam_x = 0;
|
||||
gl_cam_y = 0;
|
||||
gl_cam_z = -1;
|
||||
|
||||
gl_cam_lr = 90;
|
||||
gl_cam_ud = 0;
|
||||
}
|
||||
|
||||
void gl_main_draw(void) {
|
||||
int i;
|
||||
|
||||
glUniform1f(glGetUniformLocation(gl_shadow, "solid_color"), 1);
|
||||
|
||||
glPushMatrix();
|
||||
glRotatef(rad, 1, 0, 0);
|
||||
glRotatef(rad, 0, 1, 0);
|
||||
glRotatef(rad, 0, 0, 1);
|
||||
for(i = 0; i < 8; i++) {
|
||||
int c = (i % 2) == 0 ? 1 : 0;
|
||||
double n[3];
|
||||
double u = 0.3;
|
||||
|
||||
gl_calc_normal3(&n[0], 0, u * 1.4, 0, -u, 0, u, u, 0, u);
|
||||
|
||||
glColor3f(c ? 0.75 : 0, 0, c ? 0 : 0.75);
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
||||
glNormal3f(n[0], n[1], n[2]);
|
||||
glVertex3f(0, u * 1.4, 0);
|
||||
glVertex3f(-u, 0, u);
|
||||
glVertex3f(u, 0, u);
|
||||
glEnd();
|
||||
glRotatef(90, 0, 1, 0);
|
||||
if(i == 3) {
|
||||
glRotatef(180, 0, 0, 1);
|
||||
glRotatef(90, 0, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glColor3f(1, 1, 1);
|
||||
glBegin(GL_QUADS);
|
||||
glNormal3f(0, 1, 0);
|
||||
glVertex3f(-5, -1, -5);
|
||||
glVertex3f(-5, -1, 5);
|
||||
glVertex3f(5, -1, 5);
|
||||
glVertex3f(5, -1, -5);
|
||||
glEnd();
|
||||
|
||||
glUniform1f(glGetUniformLocation(gl_shadow, "solid_color"), 0);
|
||||
}
|
||||
|
||||
void gl_main_after(void) {
|
||||
rad += 60.0 / (1000.0 / AF_WAIT_MS);
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
static void err(const char* path, GLuint shader) {
|
||||
GLint len, ret;
|
||||
char* e;
|
||||
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
|
||||
|
||||
e = malloc(len);
|
||||
e[len] = 0;
|
||||
|
||||
glGetShaderInfoLog(shader, len, &ret, e);
|
||||
|
||||
fprintf(stderr, "%s: %s\n", path, e);
|
||||
|
||||
free(e);
|
||||
}
|
||||
|
||||
int gl_shader(GLuint* r, const char* vs, const char* fs) {
|
||||
GLuint vsi;
|
||||
GLuint fsi;
|
||||
FILE* f;
|
||||
int vsl = 0;
|
||||
int fsl = 0;
|
||||
char* vss;
|
||||
char* fss;
|
||||
GLint st;
|
||||
|
||||
if((f = fopen(vs, "rb")) != NULL) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
vsl = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
vss = malloc(vsl + 1);
|
||||
fread(vss, vsl, 1, f);
|
||||
vss[vsl] = 0;
|
||||
|
||||
fclose(f);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if((f = fopen(fs, "rb")) != NULL) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
fsl = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
fss = malloc(fsl + 1);
|
||||
fread(fss, fsl, 1, f);
|
||||
fss[fsl] = 0;
|
||||
|
||||
fclose(f);
|
||||
} else {
|
||||
free(vss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
vsi = glCreateShader(GL_VERTEX_SHADER);
|
||||
fsi = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
glShaderSource(vsi, 1, (const GLchar**)&vss, NULL);
|
||||
glShaderSource(fsi, 1, (const GLchar**)&fss, NULL);
|
||||
|
||||
glCompileShader(vsi);
|
||||
glGetShaderiv(vsi, GL_COMPILE_STATUS, &st);
|
||||
if(!st) {
|
||||
err(vs, vsi);
|
||||
|
||||
glDeleteShader(vsi);
|
||||
glDeleteShader(fsi);
|
||||
free(vss);
|
||||
free(fss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
glCompileShader(fsi);
|
||||
glGetShaderiv(fsi, GL_COMPILE_STATUS, &st);
|
||||
if(!st) {
|
||||
err(fs, fsi);
|
||||
|
||||
glDeleteShader(vsi);
|
||||
glDeleteShader(fsi);
|
||||
free(vss);
|
||||
free(fss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(vss);
|
||||
free(fss);
|
||||
|
||||
*r = glCreateProgram();
|
||||
|
||||
glAttachShader(*r, vsi);
|
||||
glAttachShader(*r, fsi);
|
||||
|
||||
glDeleteShader(vsi);
|
||||
glDeleteShader(fsi);
|
||||
|
||||
glLinkProgram(*r);
|
||||
glGetProgramiv(*r, GL_LINK_STATUS, &st);
|
||||
if(!st) {
|
||||
glDeleteProgram(*r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
void gl_util_begin_2d(void) {
|
||||
glUseProgram(0);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, MwGetInteger(opengl, MwNwidth), MwGetInteger(opengl, MwNheight), 0, -1, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
}
|
||||
|
||||
void gl_util_end_2d(void) {
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glUseProgram(gl_shadow);
|
||||
}
|
||||
103
client/main.c
103
client/main.c
|
|
@ -1,103 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
MwWidget root, window, opengl;
|
||||
|
||||
static const MwRect sizes[] = {
|
||||
{0, 0, 640, 480},
|
||||
{0, 0, 800, 600},
|
||||
{0, 0, 1024, 768},
|
||||
{0, 0, 1280, 1024},
|
||||
};
|
||||
|
||||
static void resize(MwWidget handle, void* user, void* client) {
|
||||
int ww = MwGetInteger(window, MwNwidth);
|
||||
int wh = MwGetInteger(window, MwNheight);
|
||||
|
||||
MwVaApply(opengl,
|
||||
MwNwidth, ww,
|
||||
MwNheight, wh,
|
||||
NULL);
|
||||
|
||||
gl_resize(ww, wh);
|
||||
}
|
||||
|
||||
static void tick(MwWidget handle, void* user, void* client) {
|
||||
net_poll();
|
||||
|
||||
gl_render();
|
||||
|
||||
MwOpenGLSwapBuffer(opengl);
|
||||
}
|
||||
|
||||
static void* gl_load(const char* name) {
|
||||
return MwOpenGLGetProcAddress(opengl, name);
|
||||
}
|
||||
|
||||
void scene_change(int scene) {
|
||||
gl_scene = scene;
|
||||
|
||||
gl_scene_change();
|
||||
ui_scene_change();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
MwSizeHints sh;
|
||||
MwRect rc;
|
||||
int i;
|
||||
|
||||
MwLibraryInit();
|
||||
|
||||
root = MwVaCreateWidget(NULL, "root", NULL, 0, 0, 0, 0,
|
||||
MwNwaitMS, AF_WAIT_MS,
|
||||
MwNbitmapFont, 0,
|
||||
MwNmodernLook, 1,
|
||||
NULL);
|
||||
|
||||
window = MwVaCreateWidget(MwWindowClass, "main", root, 0, 0, 640, 480,
|
||||
MwNtitle, "AssaultFish",
|
||||
NULL);
|
||||
|
||||
MwGetScreenSize(window, &rc);
|
||||
|
||||
for(i = sizeof(sizes) / sizeof(sizes[0]) - 1; i >= 0; i--) {
|
||||
if(sizes[i].width < rc.width && sizes[i].height < rc.height) {
|
||||
sh.min_width = sh.max_width = sizes[i].width;
|
||||
sh.min_height = sh.max_height = sizes[i].height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 1; i < argc; i++) {
|
||||
if(strcmp(argv[i], "--width") == 0 && (i + 1) < argc) {
|
||||
sh.min_width = sh.max_width = atoi(argv[i + 1]);
|
||||
} else if(strcmp(argv[i], "--height") == 0 && (i + 1) < argc) {
|
||||
sh.min_height = sh.max_height = atoi(argv[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
MwVaApply(window,
|
||||
MwNsizeHints, &sh,
|
||||
MwNx, (rc.width - sh.max_width) / 2,
|
||||
MwNy, (rc.height - sh.max_height) / 2,
|
||||
MwNwidth, sh.max_width,
|
||||
MwNheight, sh.max_height,
|
||||
NULL);
|
||||
|
||||
opengl = MwCreateWidget(MwOpenGLClass, "opengl", window, 0, 0, sh.max_width, sh.max_height);
|
||||
|
||||
MwOpenGLMakeCurrent(opengl);
|
||||
|
||||
gladLoadGLLoader(gl_load);
|
||||
|
||||
resize(window, NULL, NULL);
|
||||
|
||||
gl_init();
|
||||
ui_init();
|
||||
|
||||
scene_change(AF_SCENE_MAIN);
|
||||
|
||||
MwAddUserHandler(window, MwNresizeHandler, resize, NULL);
|
||||
MwAddUserHandler(root, MwNtickHandler, tick, NULL);
|
||||
|
||||
MwLoop(root);
|
||||
}
|
||||
107
client/net.c
107
client/net.c
|
|
@ -1,107 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
static fishsoup_client_t* client = NULL;
|
||||
|
||||
enum OPAQUE_STATES {
|
||||
OPAQUE_WAITING_HELLO = 0,
|
||||
OPAQUE_JOINING
|
||||
};
|
||||
|
||||
typedef struct opaque {
|
||||
char username[256];
|
||||
int state;
|
||||
} opaque_t;
|
||||
|
||||
static void net_return(MwWidget handle, void* user, void* client) {
|
||||
scene_change(AF_SCENE_MAIN);
|
||||
}
|
||||
|
||||
static void on_packet(fishsoup_client_t* self, fishsoup_packet_t* pkt) {
|
||||
opaque_t* o = self->user;
|
||||
|
||||
if(pkt->header.type == AF_PACKET_HELLO && pkt->header.length == sizeof(af_packet_hello_t) && o->state == OPAQUE_WAITING_HELLO) {
|
||||
af_packet_hello_t* p_hello = pkt->data;
|
||||
unsigned char mj, mi, pt;
|
||||
|
||||
af_common_version(&mj, &mi, &pt);
|
||||
|
||||
af_common_log_info("HELLO packet: Server version %hhu.%hhu.%hhu\n", p_hello->major, p_hello->minor, p_hello->patch);
|
||||
|
||||
if(mj == p_hello->major && mi >= p_hello->minor && (mi == p_hello->minor ? (pt >= p_hello->patch) : (1))) {
|
||||
fishsoup_packet_t p;
|
||||
af_packet_join_t p_join;
|
||||
|
||||
af_common_log_info("Server version is compatible with this client\n");
|
||||
|
||||
p.header.type = AF_PACKET_JOIN;
|
||||
p.header.length = sizeof(p_join);
|
||||
p.data = &p_join;
|
||||
|
||||
memcpy(p_join.username, o->username, 256);
|
||||
|
||||
fishsoup_packet_send(self->fd, &p);
|
||||
|
||||
o->state = OPAQUE_JOINING;
|
||||
|
||||
gl_set_status("Joining");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
af_common_log_info("Server version is incompatible with this client\n");
|
||||
MwAddUserHandler(ui_message("Server version is incompatible with this client"), MwNactivateHandler, net_return, NULL);
|
||||
fishsoup_client_destroy(self);
|
||||
client = NULL;
|
||||
} else if(pkt->header.type == AF_PACKET_OK && pkt->header.length == sizeof(af_packet_ok_t) && o->state == OPAQUE_JOINING) {
|
||||
af_packet_ok_t* p_ok = pkt->data;
|
||||
|
||||
af_common_log_info("OK Packet: Joined successfully\n");
|
||||
|
||||
gl_set_status("Downloading data");
|
||||
} else if(pkt->header.type == AF_PACKET_ERROR && pkt->header.length == sizeof(af_packet_error_t) && o->state == OPAQUE_JOINING) {
|
||||
af_packet_error_t* p_error = pkt->data;
|
||||
|
||||
af_common_log_info("ERROR Packet: Failed to join\n");
|
||||
|
||||
fishsoup_client_destroy(self);
|
||||
client = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_disconnect(fishsoup_client_t* self) {
|
||||
free(self->user);
|
||||
}
|
||||
|
||||
void net_connect(const char* username, const char* hostname, int port) {
|
||||
opaque_t* o;
|
||||
|
||||
scene_change(AF_SCENE_CONNECTING);
|
||||
client = fishsoup_client(hostname, port == 0 ? AF_DEFAULT_PORT : port);
|
||||
if(client == NULL) {
|
||||
MwAddUserHandler(ui_message("Failed to connect"), MwNactivateHandler, net_return, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
o = client->user = malloc(sizeof(opaque_t));
|
||||
o->state = OPAQUE_WAITING_HELLO;
|
||||
memset(o->username, 0, 256);
|
||||
memcpy(o->username, username, strlen(username));
|
||||
|
||||
fishsoup_client_on_packet(client, on_packet);
|
||||
fishsoup_client_on_disconnect(client, on_disconnect);
|
||||
|
||||
gl_set_status("Waiting for server to be ready");
|
||||
}
|
||||
|
||||
void net_poll(void) {
|
||||
int ret;
|
||||
if(client == NULL) return;
|
||||
|
||||
while(client != NULL && (ret = fishsoup_client_poll(client)) > 0);
|
||||
if(ret < 0) {
|
||||
MwAddUserHandler(ui_message("Connection destroyed"), MwNactivateHandler, net_return, NULL);
|
||||
fishsoup_client_destroy(client);
|
||||
client = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
125
client/ui.c
125
client/ui.c
|
|
@ -1,125 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
MwWidget ui_notice(int width, int height) {
|
||||
if(width == 0 && height == 0) {
|
||||
width = 400;
|
||||
height = 300;
|
||||
}
|
||||
|
||||
return MwVaCreateWidget(MwFrameClass, "notice", window, (MwGetInteger(window, MwNwidth) - width) / 2, (MwGetInteger(window, MwNheight) - height) / 2, width, height,
|
||||
MwNhasBorder, 1,
|
||||
MwNinverted, 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void ui_notice_destroy(MwWidget widget) {
|
||||
MwDestroyWidget(widget);
|
||||
}
|
||||
|
||||
static void ui_login_join(MwWidget handle, void* user, void* client) {
|
||||
char* username = MwStringDuplicate(MwGetText(MwGetVoid(user, "VusernameEntry"), MwNtext));
|
||||
char* hostname = MwStringDuplicate(MwGetText(MwGetVoid(user, "VhostnameEntry"), MwNtext));
|
||||
int port = atoi(MwGetText(MwGetVoid(user, "VportEntry"), MwNtext));
|
||||
|
||||
MwLLDestroyPixmap(MwGetVoid(user, MwNpixmap));
|
||||
ui_notice_destroy(user);
|
||||
|
||||
net_connect(username, hostname, port);
|
||||
|
||||
free(username);
|
||||
free(hostname);
|
||||
}
|
||||
|
||||
static void ui_login(void) {
|
||||
MwWidget wnd = ui_notice(0, 0);
|
||||
int bw = MwDefaultBorderWidth(wnd);
|
||||
MwWidget join = MwVaCreateWidget(MwButtonClass, "join", wnd, MwGetInteger(wnd, MwNwidth) - bw - 10 - 96, MwGetInteger(wnd, MwNheight) - bw - 10 - 24, 96, 24,
|
||||
MwNtext, "Join",
|
||||
NULL);
|
||||
MwWidget box = MwVaCreateWidget(MwBoxClass, "box", wnd, bw, bw, MwGetInteger(wnd, MwNwidth) - bw * 2, MwGetInteger(wnd, MwNheight) - bw * 2 - 10 - 24,
|
||||
MwNorientation, MwVERTICAL,
|
||||
MwNmargin, 10,
|
||||
MwNpadding, 10,
|
||||
NULL);
|
||||
MwWidget icon = MwVaCreateWidget(MwImageClass, "icon", box, 0, 0, 0, 0,
|
||||
MwNfillArea, 0,
|
||||
MwNfixedSize, 100,
|
||||
NULL);
|
||||
MwWidget entrybox[3];
|
||||
MwWidget entryinput[3];
|
||||
MwLLPixmap px = MwLoadImage(wnd, DATAROOTDIR "/assaultfish/logo.png");
|
||||
const char* labels[3] = {
|
||||
"Username",
|
||||
"Hostname",
|
||||
"Port"};
|
||||
const char* entries[3] = {
|
||||
"JohnDoe",
|
||||
"127.0.0.1",
|
||||
"",
|
||||
};
|
||||
int i;
|
||||
char buf[16];
|
||||
|
||||
sprintf(buf, "%d", AF_DEFAULT_PORT);
|
||||
|
||||
entries[2] = buf;
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
entrybox[i] = MwVaCreateWidget(MwBoxClass, "entrybox", box, 0, 0, 0, 0,
|
||||
MwNfixedSize, 24,
|
||||
MwNmargin, 10,
|
||||
NULL);
|
||||
MwVaCreateWidget(MwLabelClass, "label", entrybox[i], 0, 0, 0, 0,
|
||||
MwNfixedSize, 100,
|
||||
MwNtext, labels[i],
|
||||
MwNalignment, MwALIGNMENT_END,
|
||||
NULL);
|
||||
entryinput[i] = MwVaCreateWidget(MwEntryClass, "entry", entrybox[i], 0, 0, 0, 0,
|
||||
MwNtext, entries[i],
|
||||
NULL);
|
||||
}
|
||||
|
||||
MwVaApply(icon,
|
||||
MwNpixmap, px,
|
||||
NULL);
|
||||
|
||||
MwVaApply(wnd,
|
||||
MwNpixmap, px,
|
||||
"VusernameEntry", entryinput[0],
|
||||
"VhostnameEntry", entryinput[1],
|
||||
"VportEntry", entryinput[2],
|
||||
NULL);
|
||||
|
||||
MwAddUserHandler(join, MwNactivateHandler, ui_login_join, wnd);
|
||||
}
|
||||
|
||||
static void ui_message_ok(MwWidget handle, void* user, void* client) {
|
||||
MwDispatchUserHandler(user, MwNactivateHandler, NULL);
|
||||
|
||||
ui_notice_destroy(user);
|
||||
}
|
||||
|
||||
MwWidget ui_message(const char* message) {
|
||||
MwWidget wnd = ui_notice(0, 0);
|
||||
int bw = MwDefaultBorderWidth(wnd);
|
||||
MwWidget label = MwVaCreateWidget(MwLabelClass, "label", wnd, bw, (MwGetInteger(wnd, MwNheight) - 24) / 2, MwGetInteger(wnd, MwNwidth) - bw * 2, 24,
|
||||
MwNtext, message,
|
||||
NULL);
|
||||
MwWidget ok = MwVaCreateWidget(MwButtonClass, "ok", wnd, MwGetInteger(wnd, MwNwidth) - bw - 10 - 96, MwGetInteger(wnd, MwNheight) - bw - 10 - 24, 96, 24,
|
||||
MwNtext, "OK",
|
||||
NULL);
|
||||
|
||||
MwAddUserHandler(ok, MwNactivateHandler, ui_message_ok, wnd);
|
||||
|
||||
return wnd;
|
||||
}
|
||||
|
||||
void ui_scene_change(void) {
|
||||
if(gl_scene == AF_SCENE_MAIN) {
|
||||
ui_login();
|
||||
}
|
||||
}
|
||||
|
||||
void ui_init(void) {
|
||||
MwToggleDarkTheme(root, 1);
|
||||
}
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
#define STB_DS_IMPLEMENTATION
|
||||
#include <stb_ds.h>
|
||||
#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)
|
||||
1
external/libfishsoup
vendored
1
external/libfishsoup
vendored
|
|
@ -1 +0,0 @@
|
|||
Subproject commit b3ae87cf7e3e8244ec6d48f983af3e575ecd39bd
|
||||
2
external/stb/stb_image.c
vendored
2
external/stb/stb_image.c
vendored
|
|
@ -1,2 +0,0 @@
|
|||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#ifndef __AF_CLIENT_H__
|
||||
#define __AF_CLIENT_H__
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <Mw/Milsko.h>
|
||||
#include <Mw/Widget/OpenGL.h>
|
||||
|
||||
#include <GL/glu.h>
|
||||
|
||||
#define AF_WAIT_MS 15
|
||||
|
||||
enum AF_SCENES {
|
||||
AF_SCENE_MAIN = 0,
|
||||
AF_SCENE_CONNECTING
|
||||
};
|
||||
|
||||
/* main.c */
|
||||
extern MwWidget root, window, opengl;
|
||||
|
||||
void scene_change(int scene);
|
||||
|
||||
/* gl.c */
|
||||
typedef struct gl_scene {
|
||||
void (*changed)(void);
|
||||
void (*init)(void);
|
||||
void (*draw)(void);
|
||||
void (*after)(void);
|
||||
} gl_scene_t;
|
||||
|
||||
extern int gl_scene;
|
||||
extern gl_scene_t gl_scenes[];
|
||||
extern double gl_cam_lr;
|
||||
extern double gl_cam_ud;
|
||||
extern double gl_cam_x;
|
||||
extern double gl_cam_y;
|
||||
extern double gl_cam_z;
|
||||
extern GLuint gl_shadow;
|
||||
|
||||
void gl_init(void);
|
||||
void gl_render(void);
|
||||
void gl_resize(int width, int height);
|
||||
void gl_scene_change(void);
|
||||
void gl_calc_normal3(double* out, double v00, double v01, double v02, double v10, double v11, double v12, double v20, double v21, double v22);
|
||||
void gl_calc_normal4(double* out, double v00, double v01, double v02, double v10, double v11, double v12, double v20, double v21, double v22, double v30, double v31, double v32);
|
||||
void gl_set_status(const char* status);
|
||||
|
||||
/* gl_font.c */
|
||||
void gl_font_init(void);
|
||||
void gl_font_text(const char* text, int x, int y, double scale);
|
||||
int gl_font_width(const char* text);
|
||||
int gl_font_height(const char* text);
|
||||
|
||||
/* gl_main.c */
|
||||
void gl_main_changed(void);
|
||||
void gl_main_init(void);
|
||||
void gl_main_draw(void);
|
||||
void gl_main_after(void);
|
||||
|
||||
/* gl_util.c */
|
||||
void gl_util_begin_2d(void);
|
||||
void gl_util_end_2d(void);
|
||||
|
||||
/* gl_shader.c */
|
||||
int gl_shader(GLuint* r, const char* vs, const char* fs);
|
||||
|
||||
/* net.c */
|
||||
void net_connect(const char* username, const char* hostname, int port);
|
||||
void net_poll(void);
|
||||
|
||||
/* ui.c */
|
||||
MwWidget ui_notice(int width, int height);
|
||||
void ui_notice_destroy(MwWidget widget);
|
||||
void ui_init(void);
|
||||
void ui_scene_change(void);
|
||||
MwWidget ui_message(const char* message);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
#ifndef __AF_COMMON_H__
|
||||
#define __AF_COMMON_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <fishsoup.h>
|
||||
|
||||
#include <af_config.h>
|
||||
|
||||
#ifdef AF_SERVER
|
||||
#include <af_server.h>
|
||||
#endif
|
||||
|
||||
#ifdef AF_CLIENT
|
||||
#include <af_client.h>
|
||||
#endif
|
||||
|
||||
#define AF_DEFAULT_PORT 5312
|
||||
#define AF_VERSION "0.0.0"
|
||||
#define AF_COPYRIGHT "Copyright (C) 2025-2026 Pyrite development team"
|
||||
|
||||
enum AF_USER_PACKETS {
|
||||
AF_PACKET_HELLO = FISHSOUP_PACKET_USER,
|
||||
AF_PACKET_OK,
|
||||
AF_PACKET_ERROR,
|
||||
AF_PACKET_JOIN
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct af_packet_hello {
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
unsigned char patch;
|
||||
} af_packet_hello_t;
|
||||
|
||||
typedef struct af_packet_ok {
|
||||
char reason[256];
|
||||
} af_packet_ok_t;
|
||||
|
||||
typedef struct af_packet_error {
|
||||
char reason[256];
|
||||
} af_packet_error_t;
|
||||
|
||||
typedef struct af_packet_join {
|
||||
char username[256];
|
||||
} af_packet_join_t;
|
||||
#pragma pack()
|
||||
|
||||
/* version.c */
|
||||
void af_common_version(unsigned char* v_major, unsigned char* v_minor, unsigned char* v_patch);
|
||||
|
||||
/* log.c */
|
||||
void af_common_log_info(const char* fmt, ...);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef __AF_CONFIG_H__
|
||||
#define __AF_CONFIG_H__
|
||||
|
||||
#define DATAROOTDIR "@CMAKE_INSTALL_FULL_DATAROOTDIR@"
|
||||
|
||||
#endif
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef __AF_SERVER_H__
|
||||
#define __AF_SERVER_H__
|
||||
|
||||
/* net.c */
|
||||
void net_start(int port);
|
||||
void net_loop(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int port = 0;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
net_start(port);
|
||||
|
||||
net_loop();
|
||||
}
|
||||
119
server/net.c
119
server/net.c
|
|
@ -1,119 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
#include <stb_ds.h>
|
||||
|
||||
static fishsoup_server_t* server;
|
||||
|
||||
typedef struct opaque {
|
||||
char username[256 + 1];
|
||||
} opaque_t;
|
||||
|
||||
static void on_packet(fishsoup_client_t* self, fishsoup_packet_t* pkt) {
|
||||
opaque_t* o = self->user;
|
||||
|
||||
if(pkt->header.type == AF_PACKET_JOIN && pkt->header.length == sizeof(af_packet_join_t)) {
|
||||
af_packet_join_t* p_join = pkt->data;
|
||||
int i;
|
||||
|
||||
o->username[256] = 0;
|
||||
memcpy(o->username, p_join->username, 256);
|
||||
|
||||
af_common_log_info("Client %d: JOIN packet: Username \"%s\"\n", self->fd, o->username);
|
||||
|
||||
for(i = 0; i < arrlen(server->clients); i++) {
|
||||
if(self->fd != server->clients[i].fd && strcmp(o->username, ((opaque_t*)server->clients[i].user)->username) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i == arrlen(server->clients)) {
|
||||
fishsoup_packet_t p;
|
||||
af_packet_ok_t p_ok;
|
||||
|
||||
p.header.type = AF_PACKET_OK;
|
||||
p.header.length = sizeof(p_ok);
|
||||
p.data = &p_ok;
|
||||
|
||||
memset(p_ok.reason, 0, 256);
|
||||
|
||||
fishsoup_packet_send(self->fd, &p);
|
||||
|
||||
af_common_log_info("Client %d: JOIN packet: Username is unused\n", self->fd);
|
||||
} else {
|
||||
fishsoup_packet_t p;
|
||||
af_packet_error_t p_error;
|
||||
|
||||
p.header.type = AF_PACKET_ERROR;
|
||||
p.header.length = sizeof(p_error);
|
||||
p.data = &p_error;
|
||||
|
||||
memset(p_error.reason, 0, 256);
|
||||
strcpy(p_error.reason, "Username is already used");
|
||||
|
||||
fishsoup_packet_send(self->fd, &p);
|
||||
|
||||
af_common_log_info("Client %d: JOIN packet: Username is already used\n", self->fd);
|
||||
memset(o->username, 0, 256 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void on_disconnect(fishsoup_client_t* self) {
|
||||
free(self->user);
|
||||
af_common_log_info("Client %d: Disconnected\n", self->fd);
|
||||
}
|
||||
|
||||
static void on_client(fishsoup_server_t* self, fishsoup_client_t* client) {
|
||||
opaque_t* o;
|
||||
fishsoup_packet_t pkt;
|
||||
af_packet_hello_t hello;
|
||||
|
||||
af_common_log_info("Client %d: Connected\n", client->fd);
|
||||
|
||||
o = client->user = malloc(sizeof(opaque_t));
|
||||
|
||||
o->username[0] = 0;
|
||||
|
||||
fishsoup_client_on_disconnect(client, on_disconnect);
|
||||
fishsoup_client_on_packet(client, on_packet);
|
||||
|
||||
pkt.header.type = AF_PACKET_HELLO;
|
||||
pkt.header.length = sizeof(hello);
|
||||
pkt.data = &hello;
|
||||
|
||||
af_common_version(&hello.major, &hello.minor, &hello.patch);
|
||||
|
||||
fishsoup_packet_send(client->fd, &pkt);
|
||||
}
|
||||
|
||||
static struct timespec c;
|
||||
static void on_tick(fishsoup_server_t* self) {
|
||||
struct timespec n;
|
||||
unsigned long t1, t2, d;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &n);
|
||||
|
||||
t1 = n.tv_sec * 1000 + n.tv_nsec / 1000000;
|
||||
t2 = c.tv_sec * 1000 + c.tv_nsec / 1000000;
|
||||
|
||||
d = t1 - t2; /* elapsed time */
|
||||
af_common_log_info("Tick: %dms\n", d);
|
||||
|
||||
c = n;
|
||||
}
|
||||
|
||||
void net_start(int port) {
|
||||
if(port == 0) port = AF_DEFAULT_PORT;
|
||||
|
||||
if((server = fishsoup_server(port)) == NULL) return;
|
||||
|
||||
fishsoup_server_on_client(server, on_client);
|
||||
fishsoup_server_on_tick(server, on_tick);
|
||||
}
|
||||
|
||||
void net_loop(void) {
|
||||
if(server == NULL) return;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &c);
|
||||
fishsoup_server_loop(server);
|
||||
}
|
||||
14
src/log.c
14
src/log.c
|
|
@ -1,14 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
static void af_common_log(const char* type, const char* fmt, va_list va) {
|
||||
printf("[%.4s] ", type);
|
||||
vprintf(fmt, va);
|
||||
}
|
||||
|
||||
void af_common_log_info(const char* fmt, ...) {
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
af_common_log("INFO", fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#include <af_common.h>
|
||||
|
||||
void af_common_version(unsigned char* v_major, unsigned char* v_minor, unsigned char* v_patch) {
|
||||
char* mj = AF_VERSION;
|
||||
char* mi = strchr(mj, '.') + 1;
|
||||
char* pt = strchr(mi, '.') + 1;
|
||||
|
||||
*v_major = atoi(mj);
|
||||
*v_minor = atoi(mi);
|
||||
*v_patch = atoi(pt);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue