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