Annotation of brogue-ce/src/platform/web-platform.c, Revision 1.1
1.1 ! rubenllo 1: #define _POSIX_C_SOURCE 199309L
! 2:
! 3: #include <stdio.h>
! 4: #include <string.h>
! 5: #include <poll.h>
! 6: #include <unistd.h>
! 7: #include <fcntl.h>
! 8: #include <ctype.h>
! 9: #include <errno.h>
! 10: #include <time.h>
! 11: #include <sys/un.h>
! 12: #include <sys/socket.h>
! 13: #include <sys/select.h>
! 14: #include "platform.h"
! 15:
! 16: #define SERVER_SOCKET "server-socket"
! 17: #define CLIENT_SOCKET "client-socket"
! 18:
! 19: #define OUTPUT_SIZE 10
! 20:
! 21: #define EVENT_MESSAGE1_START 19
! 22: #define EVENT_MESSAGE2_START 70
! 23: #define EVENT_MESSAGE1_SIZE 51
! 24: #define EVENT_MESSAGE2_SIZE 30
! 25: #define EVENT_SIZE 100
! 26: #define MAX_INPUT_SIZE 5
! 27: #define MOUSE_INPUT_SIZE 4
! 28: #define OUTPUT_BUFFER_SIZE 1000
! 29:
! 30: //Custom events
! 31: #define REFRESH_SCREEN 50
! 32:
! 33: enum StatusTypes
! 34: {
! 35: DEEPEST_LEVEL_STATUS,
! 36: GOLD_STATUS,
! 37: SEED_STATUS,
! 38: EASY_MODE_STATUS,
! 39: STATUS_TYPES_NUMBER
! 40: };
! 41:
! 42: extern playerCharacter rogue;
! 43: static struct sockaddr_un addr_write;
! 44: static int wfd, rfd;
! 45:
! 46: static FILE *logfile;
! 47: static unsigned char outputBuffer[OUTPUT_BUFFER_SIZE];
! 48: static int outputBufferPos = 0;
! 49: static int refreshScreenOnly = 0;
! 50:
! 51: static void gameLoop();
! 52: static void openLogfile();
! 53: static void closeLogfile();
! 54: static void writeToLog(const char *msg);
! 55: static void setupSockets();
! 56: static int readFromSocket(unsigned char *buf, int size);
! 57: static void writeToSocket(unsigned char *buf, int size);
! 58: static void flushOutputBuffer();
! 59:
! 60: static void gameLoop() {
! 61: openLogfile();
! 62: writeToLog("Logfile started\n");
! 63:
! 64: setupSockets();
! 65:
! 66: rogueMain();
! 67:
! 68: closeLogfile();
! 69: }
! 70:
! 71: static void openLogfile() {
! 72: logfile = fopen("brogue-web.txt", "a");
! 73: if (logfile == NULL)
! 74: {
! 75: fprintf(stderr, "Logfile not created, errno = %d\n", errno);
! 76: }
! 77: }
! 78:
! 79: static void closeLogfile() {
! 80: fclose(logfile);
! 81: }
! 82:
! 83: static void writeToLog(const char *msg) {
! 84: fprintf(logfile, msg);
! 85: fflush(logfile);
! 86: }
! 87:
! 88: static void setupSockets() {
! 89: struct sockaddr_un addr_read;
! 90:
! 91: // Open read socket (from external)
! 92: rfd = socket(AF_UNIX, SOCK_DGRAM, 0);
! 93: remove(SERVER_SOCKET);
! 94:
! 95: memset(&addr_read, 0, sizeof(struct sockaddr_un));
! 96: addr_read.sun_family = AF_UNIX;
! 97: strncpy(addr_read.sun_path, SERVER_SOCKET, sizeof(addr_read.sun_path) - 1);
! 98:
! 99: bind(rfd, (struct sockaddr *)&addr_read, sizeof(struct sockaddr_un));
! 100:
! 101: // Open write socket (to external)
! 102: wfd = socket(AF_UNIX, SOCK_DGRAM, 0);
! 103:
! 104: memset(&addr_write, 0, sizeof(struct sockaddr_un));
! 105: addr_write.sun_family = AF_UNIX;
! 106: strncpy(addr_write.sun_path, CLIENT_SOCKET, sizeof(addr_write.sun_path) - 1);
! 107: }
! 108:
! 109: int readFromSocket(unsigned char *buf, int size) {
! 110: return recvfrom(rfd, buf, size, 0, NULL, NULL);
! 111: }
! 112:
! 113: static void flushOutputBuffer() {
! 114: char msg[80];
! 115: int no_bytes_sent;
! 116:
! 117: no_bytes_sent = sendto(wfd, outputBuffer, outputBufferPos, 0, (struct sockaddr *)&addr_write, sizeof(struct sockaddr_un));
! 118: if (no_bytes_sent == -1) {
! 119: snprintf(msg, 80, "Error: %s\n", strerror(errno));
! 120: writeToLog(msg);
! 121: } else if (no_bytes_sent != outputBufferPos) {
! 122: snprintf(msg, 80, "Sent %d bytes only - %s\n", no_bytes_sent, strerror(errno));
! 123: writeToLog(msg);
! 124: }
! 125:
! 126: outputBufferPos = 0;
! 127: }
! 128:
! 129: static void writeToSocket(unsigned char *buf, int size)
! 130: {
! 131: if (outputBufferPos + size > OUTPUT_BUFFER_SIZE) {
! 132: flushOutputBuffer();
! 133: }
! 134:
! 135: memcpy(outputBuffer + outputBufferPos, buf, size);
! 136: outputBufferPos += size;
! 137: }
! 138:
! 139: // Map characters which are missing or rendered as emoji on some platforms
! 140: static unsigned int fixUnicode(unsigned int code) {
! 141: switch (code) {
! 142: case U_ARIES: return 0x03C8;
! 143: case U_CIRCLE: return 'o';
! 144: case U_CIRCLE_BARS: return 0x25C6;
! 145: case U_FILLED_CIRCLE_BARS: return 0x25C7;
! 146: default: return code;
! 147: }
! 148: }
! 149:
! 150: static void web_plotChar(enum displayGlyph inputChar,
! 151: short xLoc, short yLoc,
! 152: short foreRed, short foreGreen, short foreBlue,
! 153: short backRed, short backGreen, short backBlue) {
! 154: unsigned char outputBuffer[OUTPUT_SIZE];
! 155: unsigned char firstCharByte, secondCharByte;
! 156: enum displayGlyph translatedChar;
! 157:
! 158: translatedChar = glyphToUnicode(inputChar);
! 159: translatedChar = fixUnicode(inputChar);
! 160:
! 161: firstCharByte = translatedChar >> 8 & 0xff;
! 162: secondCharByte = translatedChar;
! 163:
! 164: outputBuffer[0] = (unsigned char)xLoc;
! 165: outputBuffer[1] = (unsigned char)yLoc;
! 166: outputBuffer[2] = firstCharByte;
! 167: outputBuffer[3] = secondCharByte;
! 168: outputBuffer[4] = (unsigned char)foreRed * 255 / 100;
! 169: outputBuffer[5] = (unsigned char)foreGreen * 255 / 100;
! 170: outputBuffer[6] = (unsigned char)foreBlue * 255 / 100;
! 171: outputBuffer[7] = (unsigned char)backRed * 255 / 100;
! 172: outputBuffer[8] = (unsigned char)backGreen * 255 / 100;
! 173: outputBuffer[9] = (unsigned char)backBlue * 255 / 100;
! 174:
! 175: writeToSocket(outputBuffer, OUTPUT_SIZE);
! 176: }
! 177:
! 178: static void sendStatusUpdate() {
! 179: unsigned char statusOutputBuffer[OUTPUT_SIZE];
! 180: unsigned long statusValues[STATUS_TYPES_NUMBER];
! 181: int i, j;
! 182:
! 183: statusValues[DEEPEST_LEVEL_STATUS] = rogue.deepestLevel;
! 184: statusValues[GOLD_STATUS] = rogue.gold;
! 185: statusValues[SEED_STATUS] = rogue.seed;
! 186: statusValues[EASY_MODE_STATUS] = rogue.easyMode;
! 187:
! 188: memset(statusOutputBuffer, 0, OUTPUT_SIZE);
! 189:
! 190: for (i = 0; i < STATUS_TYPES_NUMBER; i++) {
! 191: // Coordinates of (255, 255) will let the server and client know that this is a status update rather than a cell update
! 192: statusOutputBuffer[0] = 255;
! 193: statusOutputBuffer[1] = 255;
! 194:
! 195: // Status type
! 196: statusOutputBuffer[2] = i;
! 197:
! 198: // Status values
! 199: statusOutputBuffer[3] = statusValues[i] >> 24 & 0xff;
! 200: statusOutputBuffer[4] = statusValues[i] >> 16 & 0xff;
! 201: statusOutputBuffer[5] = statusValues[i] >> 8 & 0xff;
! 202: statusOutputBuffer[6] = statusValues[i];
! 203:
! 204: // Fill
! 205: for (j = 7; j < OUTPUT_SIZE; j++) {
! 206: statusOutputBuffer[j] = 0;
! 207: }
! 208:
! 209: writeToSocket(statusOutputBuffer, OUTPUT_SIZE);
! 210: }
! 211: }
! 212:
! 213: // Pause by doing a blocking poll on the socket
! 214: static boolean web_pauseForMilliseconds(short milliseconds) {
! 215: fd_set input;
! 216: struct timeval timeout;
! 217:
! 218: FD_ZERO(&input);
! 219: FD_SET(rfd, &input);
! 220:
! 221: timeout.tv_sec = milliseconds / 1000;
! 222: timeout.tv_usec = (milliseconds % 1000) * 1000;
! 223:
! 224: return select(rfd + 1, &input, NULL, NULL, &timeout);
! 225: }
! 226:
! 227: static void web_nextKeyOrMouseEvent(rogueEvent *returnEvent, boolean textInput, boolean colorsDance) {
! 228:
! 229: unsigned char inputBuffer[MAX_INPUT_SIZE];
! 230: unsigned short keyCharacter;
! 231:
! 232: // Because we will halt execution until we get more input, we definitely cannot have any dancing colors from the server side.
! 233: colorsDance = false;
! 234:
! 235: // Send a status update of game variables we want on the client
! 236: if (!refreshScreenOnly) {
! 237: sendStatusUpdate();
! 238: }
! 239: refreshScreenOnly = 0;
! 240:
! 241: // Flush output buffer
! 242: flushOutputBuffer();
! 243:
! 244: // Block for next command
! 245: readFromSocket(inputBuffer, MAX_INPUT_SIZE);
! 246:
! 247: returnEvent->eventType = inputBuffer[0];
! 248:
! 249: if (returnEvent->eventType == REFRESH_SCREEN) {
! 250: // Custom event type - not a command for the brogue game
! 251: refreshScreen();
! 252: // Don't send a status update if this was only a screen refresh (may be sent by observer)
! 253: refreshScreenOnly = 1;
! 254: return;
! 255: }
! 256:
! 257: if (returnEvent->eventType == KEYSTROKE) {
! 258: keyCharacter = inputBuffer[1] << 8 | inputBuffer[2];
! 259:
! 260: // 13 is sent for RETURN on web, map to RETURN_KEY
! 261: if (keyCharacter == 13) {
! 262: keyCharacter = RETURN_KEY;
! 263: }
! 264:
! 265: returnEvent->param1 = keyCharacter;
! 266: returnEvent->controlKey = inputBuffer[3];
! 267: returnEvent->shiftKey = inputBuffer[4];
! 268: } else { // Mouse event
! 269: fread(inputBuffer, sizeof(char), MOUSE_INPUT_SIZE, stdin);
! 270: returnEvent->param1 = inputBuffer[1]; //x coord
! 271: returnEvent->param2 = inputBuffer[2]; //y coord
! 272: returnEvent->controlKey = inputBuffer[3];
! 273: returnEvent->shiftKey = inputBuffer[4];
! 274: }
! 275: }
! 276:
! 277: static void web_remap(const char *input_name, const char *output_name) {
! 278: // Not needed
! 279: }
! 280:
! 281: static boolean web_modifierHeld(int modifier) {
! 282: // Not needed, modifiers past directly with the event data
! 283: return 0;
! 284: }
! 285:
! 286: static void web_notifyEvent(short eventId, int data1, int data2, const char *str1, const char *str2) {
! 287: unsigned char statusOutputBuffer[EVENT_SIZE];
! 288:
! 289: // Coordinates of (254, 254) will let the server and client know that this is a event notification update rather than a cell update
! 290: statusOutputBuffer[0] = 254;
! 291: statusOutputBuffer[1] = 254;
! 292:
! 293: statusOutputBuffer[2] = eventId;
! 294:
! 295: statusOutputBuffer[3] = data1 >> 24 & 0xff;
! 296: statusOutputBuffer[4] = data1 >> 16 & 0xff;
! 297: statusOutputBuffer[5] = data1 >> 8 & 0xff;
! 298: statusOutputBuffer[6] = data1;
! 299: statusOutputBuffer[7] = rogue.depthLevel >> 8 & 0xff;
! 300: statusOutputBuffer[8] = rogue.depthLevel;
! 301: statusOutputBuffer[9] = rogue.easyMode >> 8 & 0xff;
! 302: statusOutputBuffer[10] = rogue.easyMode;
! 303: statusOutputBuffer[11] = rogue.gold >> 24 & 0xff;
! 304: statusOutputBuffer[12] = rogue.gold >> 16 & 0xff;
! 305: statusOutputBuffer[13] = rogue.gold >> 8 & 0xff;
! 306: statusOutputBuffer[14] = rogue.gold;
! 307: statusOutputBuffer[15] = rogue.seed >> 24 & 0xff;
! 308: statusOutputBuffer[16] = rogue.seed >> 16 & 0xff;
! 309: statusOutputBuffer[17] = rogue.seed >> 8 & 0xff;
! 310: statusOutputBuffer[18] = rogue.seed;
! 311:
! 312: // str1 is the death / victory message
! 313: memcpy(statusOutputBuffer + EVENT_MESSAGE1_START, str1, EVENT_MESSAGE1_SIZE);
! 314: statusOutputBuffer[EVENT_MESSAGE2_START - 1] = 0;
! 315: // str2 is unused
! 316: memcpy(statusOutputBuffer + EVENT_MESSAGE1_START + EVENT_MESSAGE1_SIZE, str2, EVENT_MESSAGE2_SIZE);
! 317: statusOutputBuffer[EVENT_SIZE - 1] = 0;
! 318:
! 319: writeToSocket(statusOutputBuffer, EVENT_SIZE);
! 320: flushOutputBuffer();
! 321: }
! 322:
! 323: struct brogueConsole webConsole = {
! 324: gameLoop,
! 325: web_pauseForMilliseconds,
! 326: web_nextKeyOrMouseEvent,
! 327: web_plotChar,
! 328: web_remap,
! 329: web_modifierHeld,
! 330: web_notifyEvent,
! 331: NULL,
! 332: NULL
! 333: };
CVSweb