#include #include #include #include #include #include const char * socket_path = ":2000"; static int nbeer = 0; static int trollangry = 0; /** * /tavern * /enter * /enter&hit * Queries: * ?beer */ static void tavern(const char *path, const char *query, char **title, char **header, char **result) { static const int maxbeer = 7; static const int maxmaxbeer = 12; static const int maxangry = 5; static int trollknocked = 0; fprintf(stderr, "beer: %d, troll: %d\n", nbeer, trollangry); static const char *ENTER_HIT_ACTION = "enter&hit"; if (strncmp(query, ENTER_HIT_ACTION, strlen(ENTER_HIT_ACTION)) == 0) { if (nbeer <= maxmaxbeer) { *header = "You knocked out the troll. Welcome in the tavern"; *result = "

Do you want a beer ?

" "

Or leave

"; trollknocked = 1; return; } else { *header = "You try to hit the troll"; *result = "

Your perception is damaged. You miss the troll who is upset and destroys you.

" "

Game over

"; return; } } if ((!trollknocked) && (nbeer > maxbeer)) { if (trollangry++ < maxangry) { *header = "You enter the tavern."; *result = "The troll recognizes you and throws you back in the street."; return; } else { *header = "The troll is angry"; *result = "

The troll is fed up with you and just decides to annihilate you.

" "

Game over

"; return; } } *title = "Tavern"; *header = "The tavern has a door"; *result = "You can enter"; static const char *ENTER_ACTION = "enter"; if (strncmp(query, ENTER_ACTION, strlen(ENTER_ACTION)) == 0) { *header = "Welcome in the tavern"; *result = "

Do you want a beer ?

" "

Or leave

"; return; } static const char *BEER_ACTION = "beer"; if (strncmp(query, BEER_ACTION, strlen(BEER_ACTION)) == 0) { *header = "You drink a beer"; ++nbeer; if ((nbeer <= maxbeer) || ((trollknocked) && (nbeer <= maxmaxbeer))) { *result = "

Do you want an other beer ?

" "

Or leave

"; return; } else if ((!trollknocked) && (nbeer > maxbeer)) { *result = "

You're getting drunk.

" "

The troll guard decides you'd better finish the day outside, grabs you and throw you in the street.

"; return; } else if ((trollknocked) && (nbeer > maxmaxbeer)) { *result = "

The troll wakes up behind you and decides you'd better finish the day outside, grabs you and throw you in the street.

"; trollknocked = 0; return; } } } /** /home * Queries: * ?rest */ static void home(const char *path, const char *query, char **title, char **header, char **result) { *title = "Home"; static const char *REST_ACTION = "rest"; if (strncmp(query, REST_ACTION, strlen(REST_ACTION)) == 0) { nbeer = 0; trollangry = 0; *header = "You have a rest."; *result = "You feel better. You can go in town."; return; } *header = "Home."; *result = "

You can have a rest.

" "

You can go in town.

"; } /** /library * Queries: * ?bestiary * ?lemoncake * Return 0 if data is html or 1 if it is json */ char *data_creatures; char *data_lemoncake; static int library(const char *path, const char *query, char **title, char **header, char **result, char **data) { fprintf(stderr, "library\n"); *title = "Library"; static const char *BEAST_ACTION = "bestiary"; if (strncmp(query, BEAST_ACTION, strlen(BEAST_ACTION)) == 0) { *data = data_creatures; return 1; } static const char *LEMON_ACTION = "lemoncake"; if (strncmp(query, LEMON_ACTION, strlen(LEMON_ACTION)) == 0) { *data = data_lemoncake; return 1; } *header = "There are two books:"; *result = "" "

Or return in town.

"; return 0; } int main(void) { FCGX_Request request = {0}; int count = 0; int err = FCGX_Init(); if (err != 0) { fprintf(stderr, "Failed to initialize library\n"); return -1; } /* FCGX_Init creates a socket on standard input that we don't need. This call will close it. */ FCGX_Finish(); int socket = FCGX_OpenSocket(socket_path, 1); if (socket == -1) { fprintf(stderr, "Failed to open socket %s\n", socket_path); return -1; } err = FCGX_InitRequest(&request, socket, 0); if (err != 0) { fprintf(stderr, "Failed to initialize request\n"); OS_IpcClose(request.ipcFd, -1); return -1; } fprintf(stdout, "Waiting for request\n"); while (FCGX_Accept_r(&request) >= 0) { fprintf(stdout, "Received request\n"); char *path = FCGX_GetParam("SCRIPT_NAME", request.envp); char *query = FCGX_GetParam("QUERY_STRING", request.envp); static const char *PATH_PREFIX = "/fcgi/"; if (strncmp(path, PATH_PREFIX, strlen(PATH_PREFIX)) != 0) { fprintf(stderr, "Unexepected path prefix"); continue; } path += strlen(PATH_PREFIX); char *title = "Town"; char *header = "You are in town."; char *answer = "

You can enter the tavern

" "

Visit the library

" "

Go home

"; char *data = "{}"; static const char *PLACE_TAVERN = "tavern"; static const char *GAME_OVER = "gameover"; static const char *PLACE_TOWN = "town"; static const char *PLACE_HOME = "home"; static const char *PLACE_LIBRARY = "library"; /* /library */ if (strncmp(path, PLACE_LIBRARY, strlen(PLACE_LIBRARY)) == 0) { int format = library(path+strlen(PLACE_TAVERN), query, &title, &header, &answer, &data); if (format == 1) { FCGX_FPrintF(request.out, "Content-type: application/json\r\n" "\r\n" "%s", data); continue; } } /* /tavern */ else if (strncmp(path, PLACE_TAVERN, strlen(PLACE_TAVERN)) == 0) { tavern(path+strlen(PLACE_TAVERN), query, &title, &header, &answer); } /* /home */ else if (strncmp(path, PLACE_HOME, strlen(PLACE_HOME)) == 0) { home(path+strlen(PLACE_HOME), query, &title, &header, &answer); } /* /gameover */ else if (strncmp(path, GAME_OVER, strlen(GAME_OVER)) == 0) { title = "Game over"; header = "Game over"; answer = "You are dead"; } /* / */ else if (strncmp(path, PLACE_TOWN, strlen(PLACE_TOWN)) == 0) { header = "Yes, that's town..."; } else if (strlen(path) != 0) { title = "Lost"; header = "You are lost"; answer = "You can go back in town"; } FCGX_FPrintF(request.out, "Content-type: text/html\r\n" "\r\n" "%s" "

%s

\n" "%s", title, header, answer); } FCGX_Free(&request, 0); OS_LibShutdown(); return 0; } /* Data creatures from https://github.com/dungeons-of-moria/umoria/blob/master/src/data_creatures.cpp */ char *data_creatures = "{\"umoria creatures\": [" "{\"Giant Long-Eared Bat\": {\"experience\": 20, \"area affect\": 12, \"speed\": 13, \"sprite\": \"b\", \"damages\": [1713143931, 13, 0, 1713131616], \"level\": 26}}," "{\"Giant Gnat\": {\"experience\": 1, \"area affect\": 8, \"speed\": 13, \"sprite\": \"F\", \"damages\": [1713143963, 13, 0, 1713131616], \"level\": 26}}," "{\"Killer Green Beetle\": {\"experience\": 46, \"area affect\": 12, \"speed\": 11, \"sprite\": \"K\", \"damages\": [1713143995, 14, 0, 1713131616], \"level\": 26}}," "{\"Giant Flea\": {\"experience\": 1, \"area affect\": 8, \"speed\": 12, \"sprite\": \"F\", \"damages\": [1713144027, 14, 0, 1713131616], \"level\": 26}}," "{\"Giant White Dragon Fly\": {\"experience\": 54, \"area affect\": 20, \"speed\": 11, \"sprite\": \"F\", \"damages\": [1713144059, 14, 0, 1713131616], \"level\": 26}}," "{\"Hill Giant\": {\"experience\": 52, \"area affect\": 20, \"speed\": 11, \"sprite\": \"P\", \"damages\": [1713144091, 14, 0, 1713131616], \"level\": 26}}," "{\"Skeleton Hobgoblin\": {\"experience\": 46, \"area affect\": 20, \"speed\": 11, \"sprite\": \"s\", \"damages\": [1713144123, 14, 0, 1713131616], \"level\": 26}}," "{\"Flesh Golem\": {\"experience\": 48, \"area affect\": 12, \"speed\": 11, \"sprite\": \"g\", \"damages\": [1713144155, 14, 0, 1713131616], \"level\": 26}}," "{\"White Dragon Bat\": {\"experience\": 40, \"area affect\": 12, \"speed\": 13, \"sprite\": \"b\", \"damages\": [1713144187, 14, 0, 1713131616], \"level\": 26}}," "{\"Giant Black Louse\": {\"experience\": 1, \"area affect\": 6, \"speed\": 12, \"sprite\": \"l\", \"damages\": [1713144219, 14, 0, 1713131616], \"level\": 26}}," "{\"Guardian Naga\": {\"experience\": 60, \"area affect\": 20, \"speed\": 11, \"sprite\": \"n\", \"damages\": [1713144251, 15, 0, 1713131616], \"level\": 26}}," "{\"Giant Grey Bat\": {\"experience\": 22, \"area affect\": 12, \"speed\": 13, \"sprite\": \"b\", \"damages\": [1713144283, 15, 0, 1713131616], \"level\": 26}}," "{\"Giant Clear Centipede\": {\"experience\": 30, \"area affect\": 12, \"speed\": 11, \"sprite\": \"c\", \"damages\": [1713144315, 15, 0, 1713131616], \"level\": 26}}," "{\"Giant Yellow Tick\": {\"experience\": 48, \"area affect\": 12, \"speed\": 10, \"sprite\": \"t\", \"damages\": [1713144347, 15, 0, 1713131616], \"level\": 26}}," "{\"Giant Ebony Ant\": {\"experience\": 3, \"area affect\": 12, \"speed\": 11, \"sprite\": \"a\", \"damages\": [1713144379, 15, 0, 1713131616], \"level\": 26}}," "{\"Frost Giant\": {\"experience\": 54, \"area affect\": 20, \"speed\": 11, \"sprite\": \"P\", \"damages\": [1713144411, 15, 0, 1713131616], \"level\": 26}}," "{\"Clay Golem\": {\"experience\": 50, \"area affect\": 12, \"speed\": 11, \"sprite\": \"g\", \"damages\": [1713144443, 15, 0, 1713131616], \"level\": 26}}," "{\"Huge White Bat\": {\"experience\": 3, \"area affect\": 7, \"speed\": 12, \"sprite\": \"b\", \"damages\": [1713144475, 15, 0, 1713131616], \"level\": 26}}," "{\"Giant Tan Bat\": {\"experience\": 18, \"area affect\": 12, \"speed\": 12, \"sprite\": \"b\", \"damages\": [1713144507, 15, 0, 1713131616], \"level\": 26}}," "{\"Violet Mold\": {\"experience\": 50, \"area affect\": 2, \"speed\": 11, \"sprite\": \"m\", \"damages\": [1713144539, 15, 0, 1713131616], \"level\": 26}}," "{\"Umber Hulk\": {\"experience\": 75, \"area affect\": 20, \"speed\": 11, \"sprite\": \"U\", \"damages\": [1713144571, 16, 0, 1713131616], \"level\": 26}}," "{\"Gelatinous Cube\": {\"experience\": 36, \"area affect\": 12, \"speed\": 10, \"sprite\": \"C\", \"damages\": [1713144603, 16, 0, 1713131616], \"level\": 26}}," "{\"Giant Black Rat\": {\"experience\": 3, \"area affect\": 8, \"speed\": 11, \"sprite\": \"r\", \"damages\": [1713144635, 16, 0, 1713131616], \"level\": 26}}," "{\"Giant Green Dragon Fly\": {\"experience\": 58, \"area affect\": 20, \"speed\": 11, \"sprite\": \"F\", \"damages\": [1713144667, 16, 0, 1713131616], \"level\": 26}}," "{\"Fire Giant\": {\"experience\": 62, \"area affect\": 20, \"speed\": 11, \"sprite\": \"P\", \"damages\": [1713144699, 16, 0, 1713131616], \"level\": 26}}," "{\"Green Dragon Bat\": {\"experience\": 44, \"area affect\": 12, \"speed\": 13, \"sprite\": \"b\", \"damages\": [1713144731, 16, 0, 1713131616], \"level\": 26}}" "]" "}"; char *data_lemoncake = "{\"Lemon cake\": {\n" "\"ingredients\": {\n" "\"butter\": {\"quantity\": 120, \"unit\": \"g\"},\n" "\"sugar\": {\"quantity\": 170, \"unit\": \"g\"},\n" "\"lemons\": {\"quantity\": 2, \"unit\": \"none\"},\n" "\"eggs\": {\"quantity\": 3, \"unit\": \"none\"},\n" "\"flour\": {\"quantity\": 150, \"unit\": \"g\"}\n" "},\n" "\"recipe\": [\n" "\"Melt the butter\", \"Add the sugar and the zest of the lemons\"," "\"Add the egg yolks\", \"Add the lemon juice\", \"Add the flour slowly\"," "\"Whip the egg whites and mix it to the paste\"" "],\n" "\"cook\": {\"temperature_celcius\": 180, \"time_min\": 45}" "}}";