/* This file is term3d.c. It's written in C. */ #include #include #include /* for strcpy, memset */ #ifdef _MSC_VER #define DOMAIN AF_INET #include /* for Sleep */ #include /* or Winsock2.h */ static SOCKET s = 0; /* the socket */ #else #define DOMAIN PF_INET #include #include #include /* for inet_pton */ #include /* for inet_pton */ #include /* for recv */ #include /* for napms */ #include #include /* for fcntl */ #include /* for EWOULDBLOCK */ int errno; static int s = 0; /* the socket */ #endif #include "term3d.h" static size_t xmax = 0; static size_t ymax = 0; static size_t zmax = 0; #define CHECK(x, y, z) (\ 0 <= (x) && (x) < xmax && \ 0 <= (y) && (y) < ymax && \ 0 <= (z) && (z) < zmax) static char *p; /* points to xmax * ymax * zmax array of chars */ #define P(x, y, z) (p[(x) + xmax * ((y) + ymax * (z))]) /* For reading xmax, ymax, zmax, and keystrokes from the server. Because of the O_NONBLOCK and FIONBIO in term3d_construct, the recv will return -1 immediately instead of waiting if the user hasn't pressed a key. */ static unsigned char get_char(); static unsigned char get_char() { unsigned char c; if (recv(s, #ifdef _MSC_VER /* 2nd arg of recv is (void *) in Unix, (char *) in Microsoft */ (char *) #endif &c, 1, 0) == 1) { return c; } #ifdef _MSC_VER if (WSAGetLastError() == WSAEWOULDBLOCK) { return '\0'; /* No key was pressed. */ } fprintf(stderr, "Error after recv == %d\n", WSAGetLastError()); #else if (errno == EWOULDBLOCK) { return '\0'; /* No key was pressed. */ } perror("couldn't read a char"); #endif exit(1); } /* Return the key the user pressed. If no key was pressed, return '\0' immediately. */ char term3d_key(void) { return get_char(); } void term3d_construct(const char *ip, unsigned short port) { struct sockaddr_in term; size_t size; int i; /* for inet_pton */ char buffer[INET6_ADDRSTRLEN]; /* for inet_ntop */ #ifdef _MSC_VER unsigned long nonblock = ~(unsigned long)0; WSADATA data; #else int flags; /* returned by fcntl */ #endif #ifdef _MSC_VER /* http://msdn.microsoft.com/library/ default.asp?url=/library/en-us/winsock/wsapiref_2qr6.asp */ const int startup = WSAStartup(0x101, &data); if (startup != 0) { fprintf(stderr, "WSAStartup returned %d\n", startup); perror("unable to initialize WinSock library.\n"); exit(2); } if ((s = socket(DOMAIN, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { fprintf(stderr, "Error after socket == %d\n", WSAGetLastError()); WSACleanup(); perror("couldn't create socket"); exit(3); } #else if ((s = socket(DOMAIN, SOCK_STREAM, 0)) < 0) { perror("couldn't create socket"); exit(4); } #endif memset(&term, 0, sizeof term); /* fill with zeros */ term.sin_family = DOMAIN; /* Internet address family */ term.sin_port = htons(port); #ifdef _MSC_VER term.sin_addr.S_un.S_addr = inet_addr(ip); if (term.sin_addr.S_un.S_addr == INADDR_NONE) { fprintf(stderr, "term3d_construct: bad dotted string\n"); exit(5); } #else i = inet_pton(DOMAIN, ip, &term.sin_addr); if (i == 0) { fprintf(stderr, "term3d_construct: bad dotted string\n"); exit(6); } else if (i != 1) { perror("term3d_construct"); exit(7); } #endif #ifdef _MSC_VER strcpy(buffer, inet_ntoa(&term.sin_addr)); #else if (inet_ntop(DOMAIN, &term.sin_addr, buffer, sizeof buffer) == NULL) { perror("term3d_construct"); exit(8); } #endif printf("Trying %s...\n", buffer); if (connect(s, (const struct sockaddr *)&term, (int)sizeof term) != 0) { fprintf(stderr, "errno == %d\n", errno); perror("Couldn't connect socket"); exit(9); } printf("Connected to %s.\n", buffer); xmax = get_char(); ymax = get_char(); zmax = get_char(); printf("term3d xmax == %u, ymax == %u, zmax == %u.\n", xmax, ymax, zmax); if ((p = #ifdef _MSC_VER /* needs cast to convert (void *) to (char *) */ (char *) #endif malloc(size = xmax * ymax * zmax)) == NULL) { fprintf(stderr, "term3d_construct: couldn't allocate %d bytes.\n", size); exit(10); } for (i = 0; i < size; ++i) { p[i] = ' '; } /* All the subsequent input from the socket will be the user's keystrokes (if any). Make the input non-blocking. */ #ifdef _MSC_VER if (ioctlsocket(s, FIONBIO, &nonblock) == 0) { fprintf(stderr, "Error after ioctlsocket == %d\n", WSAGetLastError()); WSACleanup(); exit(11); } #else if ((flags = fcntl(s, F_GETFL, 0)) == -1) { perror ("fcntl(F_GETFL) failed"); exit(12); } if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) { perror ("fcntl(F_SETFL) failed"); exit(13); } #endif } void term3d_destruct(void) /* Undo everything that term3d_construct did. */ { free(p); #ifdef _MSC_VER if (closesocket(s) != 0) { fprintf(stderr, "Error after closesocket == %d\n", WSAGetLastError()); WSACleanup(); exit(14); } #else if (shutdown(s, SHUT_RDWR) != 0) { perror("Can't shutdown the socket"); exit(15); } #endif printf("Connection closed.\n"); } unsigned term3d_xmax(void) {return xmax;} unsigned term3d_ymax(void) {return ymax;} unsigned term3d_zmax(void) {return zmax;} char term3d_get(unsigned x, unsigned y, unsigned z) { if (!CHECK(x, y, z)) { fprintf(stderr, "arguments %u, %u, %u to term3d_get " "out of range\n", x, y, z); exit(16); } return P(x, y, z); } void term3d_put(unsigned x, unsigned y, unsigned z, char c) { char buffer[4]; if (!CHECK(x, y, z)) { fprintf(stderr, "arguments %u, %u, %u, '%c' to term3d_put " "out of range\n", x, y, z, c); exit(17); } P(x, y, z) = c; buffer[0] = x; buffer[1] = y; buffer[2] = z; buffer[3] = c; if (send(s, buffer, sizeof buffer, 0) != sizeof buffer) { #ifdef _MSC_VER fprinf(stderr, "Error after send == %d\n", WSAGetLastError()); #endif perror("couldn't write to the server"); exit(18); } } void term3d_beep(void) { term3d_put(0, 0, 0, '\a'); } void term3d_puts(unsigned x, unsigned y, unsigned z, const char *str) { if (!CHECK(x, y, z)) { fprintf(stderr, "arguments %u, %u, %u, \"%s\" to term3d_puts " "out of range\n", x, y, z, str); exit(19); } for (; *str != '\0'; ++str) { term3d_put(x, y, z, *str); if (++x >= xmax) { x = 0; if (++y >= ymax) { y = 0; if (++z >= zmax) { z = 0; } } } } } /* Wait a fraction of a second. 1000 milliseconds == 1 second. */ void term3d_wait(int milliseconds) { #ifdef _MSC_VER Sleep(milliseconds); #else napms(milliseconds); #endif }