diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c35d3d..67e12fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,5 +13,5 @@ ADD_SUBDIRECTORY(enet) add_executable(server server.c) target_link_libraries(server ${ENet_LIBRARIES}) -add_executable(client client.c) +add_executable(client client.c rlutil.h) target_link_libraries(client ${ENet_LIBRARIES}) diff --git a/client.c b/client.c index ce679c6..cfe3b9b 100644 --- a/client.c +++ b/client.c @@ -1,6 +1,12 @@ #include #include +#include "rlutil.h" + + +// Simple LAN chat client +// The client sends string messages to the server, and the server passes +// them on to all clients ENetHost *host; @@ -57,9 +63,59 @@ int main(int argc, char *argv[]) // Send a greeting send_string(peer, "Hello, my name is Inigo Montoya"); - enet_host_service(host, &event, 0); - // TODO: event/input loop + memset(buf, 0, sizeof buf); + int check; + do + { + Sleep(1); + const int k = nb_getch(); + if (k == KEY_ENTER || k == '\r') + { + // Exit the client on text command + if (strcmp(buf, "quit") == 0 || strcmp(buf, "exit") == 0) + { + break; + } + // If we have something to say, say it to the server + if (strlen(buf) > 0) + { + send_string(peer, buf); + } + memset(buf, 0, sizeof buf); + printf("\n"); + } + else if (k > 0 && strlen(buf) < 255) + { + // Hold on to our message until we press enter + buf[strlen(buf)] = (char)k; + printf("%c", k); + } + + // Check for new messages from the server; if there are any + // just print them out + ENetEvent event; + check = enet_host_service(host, &event, 0); + if (check > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_RECEIVE: + printf("%s\n", event.packet->data); + break; + case ENET_EVENT_TYPE_DISCONNECT: + printf("Lost connection with server\n"); + break; + default: + break; + } + } + else if (check < 0) + { + fprintf(stderr, "Error servicing host\n"); + } + } + while (check >= 0); // Shut down client printf("Client closing\n"); diff --git a/rlutil.License.txt b/rlutil.License.txt new file mode 100644 index 0000000..572ab28 --- /dev/null +++ b/rlutil.License.txt @@ -0,0 +1,15 @@ +Title: License + +> DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +> Version 2, December 2004 +> +>Copyright (C) 2010 Tapio Vierros +> +>Everyone is permitted to copy and distribute verbatim or modified +>copies of this license document, and changing it is allowed as long +>as the name is changed. +> +> DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +> +>0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/rlutil.h b/rlutil.h new file mode 100644 index 0000000..20e4028 --- /dev/null +++ b/rlutil.h @@ -0,0 +1,728 @@ +#pragma once +/** + * File: rlutil.h + * + * About: Description + * This file provides some useful utilities for console mode + * roguelike game development with C and C++. It is aimed to + * be cross-platform (at least Windows and Linux). + * + * About: Copyright + * (C) 2010 Tapio Vierros + * + * About: Licensing + * See + */ + + +/// Define: RLUTIL_USE_ANSI +/// Define this to use ANSI escape sequences also on Windows +/// (defaults to using WinAPI instead). +#if 0 +#define RLUTIL_USE_ANSI +#endif + +/// Define: RLUTIL_STRING_T +/// Define/typedef this to your preference to override rlutil's string type. +/// +/// Defaults to std::string with C++ and char* with C. +#if 0 +#define RLUTIL_STRING_T char* +#endif + +#ifndef RLUTIL_INLINE + #ifdef _MSC_VER + #define RLUTIL_INLINE __inline + #else + #define RLUTIL_INLINE static __inline__ + #endif +#endif + +#ifdef __cplusplus + /// Common C++ headers + #include + #include + #include // for getch() + /// Namespace forward declarations + namespace rlutil { + RLUTIL_INLINE void locate(int x, int y); + } +#else + #include // for getch() / printf() + #include // for strlen() + RLUTIL_INLINE void locate(int x, int y); // Forward declare for C to avoid warnings +#endif // __cplusplus + +#ifdef _WIN32 + #include // for WinAPI and Sleep() + #define _NO_OLDNAMES // for MinGW compatibility + #include // for getch() and kbhit() + #define getch _getch + #define kbhit _kbhit +#else + #include // for getch() and kbhit() + #include // for getch(), kbhit() and (u)sleep() + #include // for getkey() + #include // for kbhit() + #include // for kbhit() + +/// Function: getch +/// Get character without waiting for Return to be pressed. +/// Windows has this in conio.h +RLUTIL_INLINE int getch(void) { + // Here be magic. + struct termios oldt, newt; + int ch; + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + ch = getchar(); + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + return ch; +} + +/// Function: kbhit +/// Determines if keyboard has been hit. +/// Windows has this in conio.h +RLUTIL_INLINE int kbhit(void) { + // Here be dragons. + static struct termios oldt, newt; + int cnt = 0; + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + newt.c_iflag = 0; // input mode + newt.c_oflag = 0; // output mode + newt.c_cc[VMIN] = 1; // minimum time to wait + newt.c_cc[VTIME] = 1; // minimum characters to wait for + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + ioctl(0, FIONREAD, &cnt); // Read count + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100; + select(STDIN_FILENO+1, NULL, NULL, NULL, &tv); // A small time delay + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + return cnt; // Return number of characters +} +#endif // _WIN32 + +#ifndef gotoxy +/// Function: gotoxy +/// Same as . +RLUTIL_INLINE void gotoxy(int x, int y) { + #ifdef __cplusplus + rlutil:: + #endif + locate(x,y); +} +#endif // gotoxy + +#ifdef __cplusplus +/// Namespace: rlutil +/// In C++ all functions except , and are arranged +/// under namespace rlutil. That is because some platforms have them defined +/// outside of rlutil. +namespace rlutil { +#endif + +/** + * Defs: Internal typedefs and macros + * RLUTIL_STRING_T - String type depending on which one of C or C++ is used + * RLUTIL_PRINT(str) - Printing macro independent of C/C++ + */ + +#ifdef __cplusplus + #ifndef RLUTIL_STRING_T + typedef std::string RLUTIL_STRING_T; + #endif // RLUTIL_STRING_T + + #define RLUTIL_PRINT(st) do { std::cout << st; } while(false) +#else // __cplusplus + #ifndef RLUTIL_STRING_T + typedef const char* RLUTIL_STRING_T; + #endif // RLUTIL_STRING_T + + #define RLUTIL_PRINT(st) printf("%s", st) +#endif // __cplusplus + +/** + * Enums: Color codes + * + * BLACK - Black + * BLUE - Blue + * GREEN - Green + * CYAN - Cyan + * RED - Red + * MAGENTA - Magenta / purple + * BROWN - Brown / dark yellow + * GREY - Grey / dark white + * DARKGREY - Dark grey / light black + * LIGHTBLUE - Light blue + * LIGHTGREEN - Light green + * LIGHTCYAN - Light cyan + * LIGHTRED - Light red + * LIGHTMAGENTA - Light magenta / light purple + * YELLOW - Yellow (bright) + * WHITE - White (bright) + */ +enum { + BLACK, + BLUE, + GREEN, + CYAN, + RED, + MAGENTA, + BROWN, + GREY, + DARKGREY, + LIGHTBLUE, + LIGHTGREEN, + LIGHTCYAN, + LIGHTRED, + LIGHTMAGENTA, + YELLOW, + WHITE +}; + +/** + * Consts: ANSI escape strings + * + * ANSI_CLS - Clears screen + * ANSI_ATTRIBUTE_RESET - Resets all attributes + * ANSI_CURSOR_HIDE - Hides the cursor + * ANSI_CURSOR_SHOW - Shows the cursor + * ANSI_CURSOR_HOME - Moves the cursor home (0,0) + * ANSI_BLACK - Black + * ANSI_RED - Red + * ANSI_GREEN - Green + * ANSI_BROWN - Brown / dark yellow + * ANSI_BLUE - Blue + * ANSI_MAGENTA - Magenta / purple + * ANSI_CYAN - Cyan + * ANSI_GREY - Grey / dark white + * ANSI_DARKGREY - Dark grey / light black + * ANSI_LIGHTRED - Light red + * ANSI_LIGHTGREEN - Light green + * ANSI_YELLOW - Yellow (bright) + * ANSI_LIGHTBLUE - Light blue + * ANSI_LIGHTMAGENTA - Light magenta / light purple + * ANSI_LIGHTCYAN - Light cyan + * ANSI_WHITE - White (bright) + * ANSI_BACKGROUND_BLACK - Black background + * ANSI_BACKGROUND_RED - Red background + * ANSI_BACKGROUND_GREEN - Green background + * ANSI_BACKGROUND_YELLOW - Yellow background + * ANSI_BACKGROUND_BLUE - Blue background + * ANSI_BACKGROUND_MAGENTA - Magenta / purple background + * ANSI_BACKGROUND_CYAN - Cyan background + * ANSI_BACKGROUND_WHITE - White background + */ +const RLUTIL_STRING_T ANSI_CLS = "\033[2J\033[3J"; +const RLUTIL_STRING_T ANSI_ATTRIBUTE_RESET = "\033[0m"; +const RLUTIL_STRING_T ANSI_CURSOR_HIDE = "\033[?25l"; +const RLUTIL_STRING_T ANSI_CURSOR_SHOW = "\033[?25h"; +const RLUTIL_STRING_T ANSI_CURSOR_HOME = "\033[H"; +const RLUTIL_STRING_T ANSI_BLACK = "\033[22;30m"; +const RLUTIL_STRING_T ANSI_RED = "\033[22;31m"; +const RLUTIL_STRING_T ANSI_GREEN = "\033[22;32m"; +const RLUTIL_STRING_T ANSI_BROWN = "\033[22;33m"; +const RLUTIL_STRING_T ANSI_BLUE = "\033[22;34m"; +const RLUTIL_STRING_T ANSI_MAGENTA = "\033[22;35m"; +const RLUTIL_STRING_T ANSI_CYAN = "\033[22;36m"; +const RLUTIL_STRING_T ANSI_GREY = "\033[22;37m"; +const RLUTIL_STRING_T ANSI_DARKGREY = "\033[01;30m"; +const RLUTIL_STRING_T ANSI_LIGHTRED = "\033[01;31m"; +const RLUTIL_STRING_T ANSI_LIGHTGREEN = "\033[01;32m"; +const RLUTIL_STRING_T ANSI_YELLOW = "\033[01;33m"; +const RLUTIL_STRING_T ANSI_LIGHTBLUE = "\033[01;34m"; +const RLUTIL_STRING_T ANSI_LIGHTMAGENTA = "\033[01;35m"; +const RLUTIL_STRING_T ANSI_LIGHTCYAN = "\033[01;36m"; +const RLUTIL_STRING_T ANSI_WHITE = "\033[01;37m"; +const RLUTIL_STRING_T ANSI_BACKGROUND_BLACK = "\033[40m"; +const RLUTIL_STRING_T ANSI_BACKGROUND_RED = "\033[41m"; +const RLUTIL_STRING_T ANSI_BACKGROUND_GREEN = "\033[42m"; +const RLUTIL_STRING_T ANSI_BACKGROUND_YELLOW = "\033[43m"; +const RLUTIL_STRING_T ANSI_BACKGROUND_BLUE = "\033[44m"; +const RLUTIL_STRING_T ANSI_BACKGROUND_MAGENTA = "\033[45m"; +const RLUTIL_STRING_T ANSI_BACKGROUND_CYAN = "\033[46m"; +const RLUTIL_STRING_T ANSI_BACKGROUND_WHITE = "\033[47m"; +// Remaining colors not supported as background colors + +/** + * Enums: Key codes for keyhit() + * + * KEY_ESCAPE - Escape + * KEY_ENTER - Enter + * KEY_SPACE - Space + * KEY_INSERT - Insert + * KEY_HOME - Home + * KEY_END - End + * KEY_DELETE - Delete + * KEY_PGUP - PageUp + * KEY_PGDOWN - PageDown + * KEY_UP - Up arrow + * KEY_DOWN - Down arrow + * KEY_LEFT - Left arrow + * KEY_RIGHT - Right arrow + * KEY_F1 - F1 + * KEY_F2 - F2 + * KEY_F3 - F3 + * KEY_F4 - F4 + * KEY_F5 - F5 + * KEY_F6 - F6 + * KEY_F7 - F7 + * KEY_F8 - F8 + * KEY_F9 - F9 + * KEY_F10 - F10 + * KEY_F11 - F11 + * KEY_F12 - F12 + * KEY_NUMDEL - Numpad del + * KEY_NUMPAD0 - Numpad 0 + * KEY_NUMPAD1 - Numpad 1 + * KEY_NUMPAD2 - Numpad 2 + * KEY_NUMPAD3 - Numpad 3 + * KEY_NUMPAD4 - Numpad 4 + * KEY_NUMPAD5 - Numpad 5 + * KEY_NUMPAD6 - Numpad 6 + * KEY_NUMPAD7 - Numpad 7 + * KEY_NUMPAD8 - Numpad 8 + * KEY_NUMPAD9 - Numpad 9 + */ +enum { + KEY_ESCAPE = 0, + KEY_ENTER = 1, + KEY_SPACE = 32, + + KEY_INSERT = 2, + KEY_HOME = 3, + KEY_PGUP = 4, + KEY_DELETE = 5, + KEY_END = 6, + KEY_PGDOWN = 7, + + KEY_UP = 14, + KEY_DOWN = 15, + KEY_LEFT = 16, + KEY_RIGHT = 17, + + KEY_F1 = 18, + KEY_F2 = 19, + KEY_F3 = 20, + KEY_F4 = 21, + KEY_F5 = 22, + KEY_F6 = 23, + KEY_F7 = 24, + KEY_F8 = 25, + KEY_F9 = 26, + KEY_F10 = 27, + KEY_F11 = 28, + KEY_F12 = 29, + + KEY_NUMDEL = 30, + KEY_NUMPAD0 = 31, + KEY_NUMPAD1 = 127, + KEY_NUMPAD2 = 128, + KEY_NUMPAD3 = 129, + KEY_NUMPAD4 = 130, + KEY_NUMPAD5 = 131, + KEY_NUMPAD6 = 132, + KEY_NUMPAD7 = 133, + KEY_NUMPAD8 = 134, + KEY_NUMPAD9 = 135 +}; + +/// Function: getkey +/// Reads a key press (blocking) and returns a key code. +/// +/// See +/// +/// Note: +/// Only Arrows, Esc, Enter and Space are currently working properly. +RLUTIL_INLINE int getkey(void) { + #ifndef _WIN32 + int cnt = kbhit(); // for ANSI escapes processing + #endif + int k = getch(); + switch(k) { + case 0: { + int kk; + switch (kk = getch()) { + case 71: return KEY_NUMPAD7; + case 72: return KEY_NUMPAD8; + case 73: return KEY_NUMPAD9; + case 75: return KEY_NUMPAD4; + case 77: return KEY_NUMPAD6; + case 79: return KEY_NUMPAD1; + case 80: return KEY_NUMPAD4; + case 81: return KEY_NUMPAD3; + case 82: return KEY_NUMPAD0; + case 83: return KEY_NUMDEL; + default: return kk-59+KEY_F1; // Function keys + }} + case 224: { + int kk; + switch (kk = getch()) { + case 71: return KEY_HOME; + case 72: return KEY_UP; + case 73: return KEY_PGUP; + case 75: return KEY_LEFT; + case 77: return KEY_RIGHT; + case 79: return KEY_END; + case 80: return KEY_DOWN; + case 81: return KEY_PGDOWN; + case 82: return KEY_INSERT; + case 83: return KEY_DELETE; + default: return kk-123+KEY_F1; // Function keys + }} + case 13: return KEY_ENTER; +#ifdef _WIN32 + case 27: return KEY_ESCAPE; +#else // _WIN32 + case 155: // single-character CSI + case 27: { + // Process ANSI escape sequences + if (cnt >= 3 && getch() == '[') { + switch (k = getch()) { + case 'A': return KEY_UP; + case 'B': return KEY_DOWN; + case 'C': return KEY_RIGHT; + case 'D': return KEY_LEFT; + } + } else return KEY_ESCAPE; + } +#endif // _WIN32 + default: return k; + } +} + +/// Function: nb_getch +/// Non-blocking getch(). Returns 0 if no key was pressed. +RLUTIL_INLINE int nb_getch(void) { + if (kbhit()) return getch(); + else return 0; +} + +/// Function: getANSIColor +/// Return ANSI color escape sequence for specified number 0-15. +/// +/// See +RLUTIL_INLINE RLUTIL_STRING_T getANSIColor(const int c) { + switch (c) { + case BLACK : return ANSI_BLACK; + case BLUE : return ANSI_BLUE; // non-ANSI + case GREEN : return ANSI_GREEN; + case CYAN : return ANSI_CYAN; // non-ANSI + case RED : return ANSI_RED; // non-ANSI + case MAGENTA : return ANSI_MAGENTA; + case BROWN : return ANSI_BROWN; + case GREY : return ANSI_GREY; + case DARKGREY : return ANSI_DARKGREY; + case LIGHTBLUE : return ANSI_LIGHTBLUE; // non-ANSI + case LIGHTGREEN : return ANSI_LIGHTGREEN; + case LIGHTCYAN : return ANSI_LIGHTCYAN; // non-ANSI; + case LIGHTRED : return ANSI_LIGHTRED; // non-ANSI; + case LIGHTMAGENTA: return ANSI_LIGHTMAGENTA; + case YELLOW : return ANSI_YELLOW; // non-ANSI + case WHITE : return ANSI_WHITE; + default: return ""; + } +} + +/// Function: getANSIBackgroundColor +/// Return ANSI background color escape sequence for specified number 0-15. +/// +/// See +RLUTIL_INLINE RLUTIL_STRING_T getANSIBackgroundColor(const int c) { + switch (c) { + case BLACK : return ANSI_BACKGROUND_BLACK; + case BLUE : return ANSI_BACKGROUND_BLUE; + case GREEN : return ANSI_BACKGROUND_GREEN; + case CYAN : return ANSI_BACKGROUND_CYAN; + case RED : return ANSI_BACKGROUND_RED; + case MAGENTA: return ANSI_BACKGROUND_MAGENTA; + case BROWN : return ANSI_BACKGROUND_YELLOW; + case GREY : return ANSI_BACKGROUND_WHITE; + default: return ""; + } +} + +/// Function: setColor +/// Change color specified by number (Windows / QBasic colors). +/// Don't change the background color +/// +/// See +RLUTIL_INLINE void setColor(int c) { +#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI) + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(hConsole, &csbi); + + SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFFF0) | (WORD)c); // Foreground colors take up the least significant byte +#else + RLUTIL_PRINT(getANSIColor(c)); +#endif +} + +/// Function: setBackgroundColor +/// Change background color specified by number (Windows / QBasic colors). +/// Don't change the foreground color +/// +/// See +RLUTIL_INLINE void setBackgroundColor(int c) { +#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI) + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(hConsole, &csbi); + + SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFF0F) | (((WORD)c) << 4)); // Background colors take up the second-least significant byte +#else + RLUTIL_PRINT(getANSIBackgroundColor(c)); +#endif +} + +/// Function: saveDefaultColor +/// Call once to preserve colors for use in resetColor() +/// on Windows without ANSI, no-op otherwise +/// +/// See +/// See +RLUTIL_INLINE int saveDefaultColor() { +#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI) + static char initialized = 0; // bool + static WORD attributes; + + if (!initialized) { + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + attributes = csbi.wAttributes; + initialized = 1; + } + return attributes; +#else + return -1; +#endif +} + +/// Function: resetColor +/// Reset color to default +/// Requires a call to saveDefaultColor() to set the defaults +/// +/// See +/// See +/// See +RLUTIL_INLINE void resetColor() { +#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI) + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), saveDefaultColor()); +#else + RLUTIL_PRINT(ANSI_ATTRIBUTE_RESET); +#endif +} + +/// Function: cls +/// Clears screen, resets all attributes and moves cursor home. +RLUTIL_INLINE void cls(void) { +#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI) + // Based on https://msdn.microsoft.com/en-us/library/windows/desktop/ms682022%28v=vs.85%29.aspx + const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + const COORD coordScreen = {0, 0}; + DWORD cCharsWritten; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(hConsole, &csbi); + const DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y; + FillConsoleOutputCharacter(hConsole, (TCHAR)' ', dwConSize, coordScreen, &cCharsWritten); + + GetConsoleScreenBufferInfo(hConsole, &csbi); + FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten); + + SetConsoleCursorPosition(hConsole, coordScreen); +#else + RLUTIL_PRINT(ANSI_CLS); + RLUTIL_PRINT(ANSI_CURSOR_HOME); +#endif +} + +/// Function: locate +/// Sets the cursor position to 1-based x,y. +RLUTIL_INLINE void locate(int x, int y) { +#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI) + COORD coord; + // TODO: clamping/assert for x/y <= 0? + coord.X = x - 1; + coord.Y = y - 1; // Windows uses 0-based coordinates + SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); +#else // _WIN32 || USE_ANSI + #ifdef __cplusplus + RLUTIL_PRINT("\033[" << y << ";" << x << "H"); + #else // __cplusplus + char buf[32]; + sprintf(buf, "\033[%d;%df", y, x); + RLUTIL_PRINT(buf); + #endif // __cplusplus +#endif // _WIN32 || USE_ANSI +} + +/// Function: setString +/// Prints the supplied string without advancing the cursor +#ifdef __cplusplus +RLUTIL_INLINE void setString(const RLUTIL_STRING_T & str_) { + const char * const str = str_.data(); + unsigned int len = str_.size(); +#else // __cplusplus +RLUTIL_INLINE void setString(RLUTIL_STRING_T str) { + unsigned int len = strlen(str); +#endif // __cplusplus +#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI) + HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD numberOfCharsWritten; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(hConsoleOutput, &csbi); + WriteConsoleOutputCharacter(hConsoleOutput, str, len, csbi.dwCursorPosition, &numberOfCharsWritten); +#else // _WIN32 || USE_ANSI + RLUTIL_PRINT(str); + #ifdef __cplusplus + RLUTIL_PRINT("\033[" << len << 'D'); + #else // __cplusplus + char buf[3 + 20 + 1]; // 20 = max length of 64-bit unsigned int when printed as dec + sprintf(buf, "\033[%uD", len); + RLUTIL_PRINT(buf); + #endif // __cplusplus +#endif // _WIN32 || USE_ANSI +} + +/// Function: setChar +/// Sets the character at the cursor without advancing the cursor +RLUTIL_INLINE void setChar(char ch) { + const char buf[] = {ch, 0}; + setString(buf); +} + +/// Function: setCursorVisibility +/// Shows/hides the cursor. +RLUTIL_INLINE void setCursorVisibility(char visible) { +#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI) + HANDLE hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE ); + CONSOLE_CURSOR_INFO structCursorInfo; + GetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); // Get current cursor size + structCursorInfo.bVisible = (visible ? TRUE : FALSE); + SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); +#else // _WIN32 || USE_ANSI + RLUTIL_PRINT((visible ? ANSI_CURSOR_SHOW : ANSI_CURSOR_HIDE)); +#endif // _WIN32 || USE_ANSI +} + +/// Function: hidecursor +/// Hides the cursor. +RLUTIL_INLINE void hidecursor(void) { + setCursorVisibility(0); +} + +/// Function: showcursor +/// Shows the cursor. +RLUTIL_INLINE void showcursor(void) { + setCursorVisibility(1); +} + +/// Function: msleep +/// Waits given number of milliseconds before continuing. +RLUTIL_INLINE void msleep(unsigned int ms) { +#ifdef _WIN32 + Sleep(ms); +#else + // usleep argument must be under 1 000 000 + if (ms > 1000) sleep(ms/1000000); + usleep((ms % 1000000) * 1000); +#endif +} + +/// Function: trows +/// Get the number of rows in the terminal window or -1 on error. +RLUTIL_INLINE int trows(void) { +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) + return -1; + else + return csbi.srWindow.Bottom - csbi.srWindow.Top + 1; // Window height + // return csbi.dwSize.Y; // Buffer height +#else +#ifdef TIOCGSIZE + struct ttysize ts; + ioctl(STDIN_FILENO, TIOCGSIZE, &ts); + return ts.ts_lines; +#elif defined(TIOCGWINSZ) + struct winsize ts; + ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); + return ts.ws_row; +#else // TIOCGSIZE + return -1; +#endif // TIOCGSIZE +#endif // _WIN32 +} + +/// Function: tcols +/// Get the number of columns in the terminal window or -1 on error. +RLUTIL_INLINE int tcols(void) { +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) + return -1; + else + return csbi.srWindow.Right - csbi.srWindow.Left + 1; // Window width + // return csbi.dwSize.X; // Buffer width +#else +#ifdef TIOCGSIZE + struct ttysize ts; + ioctl(STDIN_FILENO, TIOCGSIZE, &ts); + return ts.ts_cols; +#elif defined(TIOCGWINSZ) + struct winsize ts; + ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); + return ts.ws_col; +#else // TIOCGSIZE + return -1; +#endif // TIOCGSIZE +#endif // _WIN32 +} + +/// Function: anykey +/// Waits until a key is pressed. +/// In C++, it either takes no arguments +/// or a template-type-argument-deduced +/// argument. +/// In C, it takes a const char* representing +/// the message to be displayed, or NULL +/// for no message. +#ifdef __cplusplus +RLUTIL_INLINE void anykey() { + getch(); +} + +template void anykey(const T& msg) { + RLUTIL_PRINT(msg); +#else +RLUTIL_INLINE void anykey(RLUTIL_STRING_T msg) { + if (msg) + RLUTIL_PRINT(msg); +#endif // __cplusplus + getch(); +} + +// Classes are here at the end so that documentation is pretty. + +#ifdef __cplusplus +/// Class: CursorHider +/// RAII OOP wrapper for . +/// Hides the cursor and shows it again +/// when the object goes out of scope. +struct CursorHider { + CursorHider() { hidecursor(); } + ~CursorHider() { showcursor(); } +}; + +} // namespace rlutil +#endif diff --git a/server.c b/server.c index 0ee46fa..9eb1648 100644 --- a/server.c +++ b/server.c @@ -4,6 +4,11 @@ #include +// Simple LAN chat server +// Clients can send simple string messages to the server, which simply +// gets broadcast to all connected clients. + + ENetHost *host; #define MAX_CLIENTS 16 @@ -21,6 +26,13 @@ void sigint_handle(int signum) #define Sleep(x) usleep((x)*1000) #endif +void send_string(ENetHost *host, char *s) +{ + ENetPacket *packet = enet_packet_create( + s, strlen(s) + 1, ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(host, 0, packet); +} + int main(int argc, char *argv[]) { @@ -53,18 +65,25 @@ int main(int argc, char *argv[]) if (check > 0) { char buf[256]; + // Whenever a client connects or disconnects, broadcast a message + // Whenever a client says something, broadcast it including + // which client it was from switch (event.type) { case ENET_EVENT_TYPE_CONNECT: - enet_address_get_host_ip(&event.peer->address, buf, sizeof buf); - printf("New connection from %s\n", buf); + sprintf(buf, "New client connected: id %d", event.peer->incomingPeerID); + send_string(host, buf); + printf("%s\n", buf); break; case ENET_EVENT_TYPE_RECEIVE: - printf("Received data '%s'\n", event.packet->data); + sprintf(buf, "Client %d says: %s", event.peer->incomingPeerID, event.packet->data); + send_string(host, buf); + printf("%s\n", buf); break; case ENET_EVENT_TYPE_DISCONNECT: - enet_address_get_host_ip(&event.peer->address, buf, sizeof buf); - printf("Client disconnected %s\n", buf); + sprintf(buf, "Client %d disconnected", event.peer->incomingPeerID); + send_string(host, buf); + printf("%s\n", buf); break; default: break;