void server_writeto(client_t *client, const void *data, size_t size) { if (size == 0) return; traffic += size; // Fileclients werden direkt ueber write abgewickelt. if (client->is_file_writer) { write(client_num(client), data, size); return; } if (EVBUFFER_LENGTH(client->out_buf) > 1024*1024) return; if (client->compress) { char buf[1024]; client->strm.next_in = (void*)data; // not const? client->strm.avail_in = size; while (client->strm.avail_in > 0) { client->strm.next_out = (unsigned char*)buf; client->strm.avail_out = sizeof(buf); int ret = deflate(&client->strm, 0); if (ret != Z_OK) { fprintf(stderr, "urgh. deflate didn't return Z_OK: %d\n", ret); // XXX: handle } evbuffer_add(client->out_buf, buf, sizeof(buf) - client->strm.avail_out); } } else { evbuffer_add(client->out_buf, (void*)data, size); } event_add(&client->wr_event, NULL); }
DECL(int, FSOpenFile, void *pClient, void *pCmd, const char *path, const char *mode, int *handle, int error) { int my_ret = -1; error = 0xffffffff; if ((int)bss_ptr != 0x0a000000) { int client = client_num(pClient); if (client < MAX_CLIENT && client >= 0) { int ret; my_ret = cafiine_fopen(bss.socket_fsa[client], &ret, path, mode, handle); if (my_ret == 0) { // File exists in cafiine server, a new handle has been created return ret; } else if (my_ret >= 1) { // File has been requested from cafiine server, ret = real_FSOpenFile(pClient, pCmd, path, mode, handle, error); if (ret >= 0) { int size = (my_ret == 1 ? DUMP_BLOCK_SIZE : DUMP_BLOCK_SIZE_SLOW); cafiine_send_handle(bss.socket_fsa[client], client, path, *handle); void* buffer = memalign(sizeof(char) * size, 0x40); int ret2; while ((ret2 = real_FSReadFile(pClient, pCmd, buffer, 1, size, *handle, 0, error)) > 0) cafiine_send_file(bss.socket_fsa[client], buffer, ret2, *handle); cafiine_fclose(bss.socket_fsa[client], &ret2, *handle); real_FSSetPosFile(pClient, pCmd, *handle, 0, error); } return ret; } } } return real_FSOpenFile(pClient, pCmd, path, mode, handle, error); }
static int GetCurClient(void *pClient) { if ((int)bss_ptr != 0x0a000000) { int client = client_num(pClient); if (client >= 0) { return client; } } return -1; }
DECL(int, FSDelClient, void *pClient) { if ((int)bss_ptr != 0x0a000000) { int client = client_num(pClient); if (client >= 0) { fs_disconnect(bss.socket_fs[client]); client_num_free(client); } } return real_FSDelClient(pClient); }
DECL(int, FSIsEof, void *pClient, void *pCmd, int fd, int error) { if ((int)bss_ptr != 0x0a000000 && ((fd & MASK_FD) == MASK_FD)) { int client = client_num(pClient); if (client < MAX_CLIENT && client >= 0) { int ret; if (cafiine_feof(bss.socket_fsa[client], &ret, fd) == 0) { return ret; } } } return real_FSIsEof(pClient, pCmd, fd, error); }
DECL(int, FSGetStatFile, void *pClient, void *pCmd, int fd, void *buffer, int error) { if ((int)bss_ptr != 0x0a000000 && ((fd & MASK_FD) == MASK_FD)) { int client = client_num(pClient); if (client < MAX_CLIENT && client >= 0) { int ret; if (cafiine_fstat(bss.socket_fsa[client], &ret, fd, buffer) == 0) { return ret; } } } return real_FSGetStatFile(pClient, pCmd, fd, buffer, error); }
DECL(int, FSGetPosFile, void *pClient, void *pCmd, int fd, int *pos, int error) { if ((int)bss_ptr != 0x0a000000 && ((fd & MASK_FD) == MASK_FD)) { int client = client_num(pClient); if (client < MAX_CLIENT && client >= 0) { int ret; if (cafiine_fgetpos(bss.socket_fsa[client], &ret, fd, pos) == 0) { return ret; } } } return real_FSGetPosFile(pClient, pCmd, fd, pos, error); }
DECL(int, FSReadFile, void *pClient, void *pCmd, void *buffer, int size, int count, int fd, int flag, int error) { if ((int)bss_ptr != 0x0a000000 && ((fd & MASK_FD) == MASK_FD)) { int client = client_num(pClient); if (client < MAX_CLIENT && client >= 0) { int ret; if (cafiine_fread(bss.socket_fsa[client], &ret, buffer, size, count, fd) == 0) { return ret; } } } return real_FSReadFile(pClient, pCmd, buffer, size, count, fd, flag, error); }
DECL(int, FSOpenFile, void *pClient, void *pCmd, const char *path, const char *mode, int *handle, int error) { if ((int)bss_ptr != 0x0a000000) { int client = client_num(pClient); if (client < MAX_CLIENT && client >= 0) { int ret; if (cafiine_fopen(bss.socket_fsa[client], &ret, path, mode, handle) == 0) { return ret; } } } return real_FSOpenFile(pClient, pCmd, path, mode, handle, error); }
static int luaStartFileWriter(lua_State *L) { const char *file = luaL_checkstring(L, 1); int one_game = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : 1; int is_gui_client = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : 1; client_t *filewriter = server_start_file_writer(file); if (!filewriter) luaL_error(L, "cannot start file %s", file); if (is_gui_client) client_turn_into_gui_client(filewriter); if (one_game) filewriter->kick_at_end_of_game = one_game; lua_pushnumber(L, client_num(filewriter)); return 1; }
static void server_readable(int fd, short event, void *arg) { client_t *client = (client_t*)arg; // Der Client wurde 'extern' gekickt, allerdings noch // nicht entfernt. Dann wird dieser Readcallback aufgerufen, // sollte allerdings nichts mehr machen. if (client->kill_me) return; int ret = evbuffer_read(client->in_buf, fd, 128); if (ret < 0) { server_destroy(client, strerror(errno)); } else if (ret == 0) { server_destroy(client, "eof reached"); } else if (EVBUFFER_LENGTH(client->in_buf) > 8192) { server_destroy(client, "line too long. go away."); } else { char *line; while ((line = evbuffer_readline(client->in_buf))) { lua_pushliteral(L, "on_client_input"); lua_rawget(L, LUA_GLOBALSINDEX); lua_pushnumber(L, client_num(client)); lua_pushstring(L, line); free(line); // Cycles fuer die Verarbeitung hochsetzen lua_set_cycles(L, 0xFFFFFF); // Input verarbeiten output_client = client; if (lua_pcall(L, 2, 0, 0) != 0) { fprintf(stderr, "error calling on_client_input: %s\n", lua_tostring(L, -1)); server_writeto(client, lua_tostring(L, -1), lua_strlen(L, -1)); lua_pop(L, 1); } output_client = NULL; // Kill Me Flag waehrend Aufruf von on_client_input // gesetzt? Direkt rausschmeissen! if (client->kill_me) { server_destroy(client, client->kill_me); return; } } } }
client_t *server_accept(int fd, const char *address) { client_t *client = clients; for (int i = 0; i < MAXCLIENTS; i++, client++) { if (!CLIENT_USED(client)) goto found; } // write(fd, "no free slot\r\n", 14); fprintf(stderr, "cannot accept() new incoming connection: no free slot\n"); return NULL; found: memset(client, 0, sizeof(client_t)); client->fd = fd; // File Writer wird leicht unterschiedlich behandelt client->is_file_writer = strstr(address, "special:file") == address; // Non Blocking setzen #ifdef WIN32 DWORD notblock = 1; ioctlsocket(client->fd, FIONBIO, ¬block); #else if (fcntl(client->fd, F_SETFL, O_NONBLOCK) < 0) { fprintf(stderr, "cannot set accept()ed socket nonblocking: %s\n", strerror(errno)); return NULL; } #endif // Soll Verbindung angenommen werden? lua_pushliteral(L, "on_new_client"); lua_rawget(L, LUA_GLOBALSINDEX); lua_pushstring(L, address); if (lua_pcall(L, 1, 2, 0) != 0) { fprintf(stderr, "error calling on_new_client: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); return NULL; } if (!lua_toboolean(L, -2)) { size_t len; const char *msg = lua_tolstring(L, -1, &len); write(client->fd, msg, len); lua_pop(L, 2); return NULL; } lua_pop(L, 2); // Libevent aktivieren event_set(&client->rd_event, client->fd, EV_READ | EV_PERSIST, server_readable, client); event_set(&client->wr_event, client->fd, EV_WRITE , server_writable, client); client->in_buf = evbuffer_new(); client->out_buf = evbuffer_new(); client->compress = 0; client->kill_me = NULL; client->player = NULL; client->next = NULL; client->prev = NULL; client->is_gui_client = 0; client->next_gui = NULL; client->prev_gui = NULL; num_clients++; // Annehmen lua_pushliteral(L, "on_client_accepted"); lua_rawget(L, LUA_GLOBALSINDEX); lua_pushnumber(L, client_num(client)); lua_pushstring(L, address); if (lua_pcall(L, 2, 0, 0) != 0) { fprintf(stderr, "error calling on_client_accepted: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); } if (!client->is_file_writer) event_add(&client->rd_event, NULL); return client; }
void server_destroy(client_t *client, const char *reason) { lua_pushliteral(L, "on_client_close"); lua_rawget(L, LUA_GLOBALSINDEX); lua_pushnumber(L, client_num(client)); lua_pushstring(L, reason); if (lua_pcall(L, 2, 0, 0) != 0) { fprintf(stderr, "error calling on_client_close: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); } // Quitmeldung senden if (client->is_gui_client) { packet_t packet; packet_init(&packet, PACKET_QUIT_MSG); packet_writeXX(&packet, reason, strlen(reason)); server_send_packet(&packet, client); } else { server_writeto(client, "connection terminated: ", 23); server_writeto(client, reason, strlen(reason)); server_writeto(client, "\r\n", 2); } // Kompressionsrest flushen server_flush_compression(client); // Rest rausschreiben (hier keine Fehlerbehandlung mehr, da eh egal). // Bei Filewritern muss nichts geschrieben werden, da deren Daten // immer direkt rausgeschrieben werden. if (!client->is_file_writer) evbuffer_write(client->out_buf, client->fd); evbuffer_free(client->in_buf); evbuffer_free(client->out_buf); free(client->kill_me); if (client->compress) deflateEnd(&client->strm); event_del(&client->rd_event); event_del(&client->wr_event); client->in_buf = NULL; client->out_buf = NULL; if (client->player) player_detach_client(client, client->player); assert(client->next == NULL); assert(client->prev == NULL); if (client->is_gui_client) { if (client->next_gui == client) { assert(client->prev_gui == client); guiclients = NULL; } else { client->next_gui->prev_gui = client->prev_gui; client->prev_gui->next_gui = client->next_gui; guiclients = client->next_gui; } } num_clients--; #ifndef NO_CONSOLE_CLIENT if (client->fd != STDIN_FILENO) #endif #ifdef WIN32 if (client->is_file_writer) { close(client->fd); } else { closesocket(client->fd); } #else close(client->fd); #endif }