[BACK]Return to web-platform.c CVS log [TXT][DIR] Up to [contributed] / brogue-ce / src / platform

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