[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

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