Skip to content
Snippets Groups Projects
nethack.cc 10.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • slevy's avatar
    slevy committed
    #ifndef USE_NETHACK
    
    slevy's avatar
    slevy committed
    void nethack_init() { }
    
    slevy's avatar
    slevy committed
    #else /* do USE_NETHACK */
    
    slevy's avatar
    slevy committed
    
    
    /*
     * Tacky code for over-the-network control of partiview.
     * Use multi- or unicast UDP.
     *
     * Stuart Levy, slevy@ncsa.uiuc.edu
     * National Center for Supercomputing Applications,
     * University of Illinois 2001.
    
     * This file is part of partiview, released under the
     * Illinois Open Source License; see the file LICENSE.partiview for details.
    
    slevy's avatar
    slevy committed
    #include "config.h"
    
    slevy's avatar
    slevy committed
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <net/if.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <errno.h>
    #include <signal.h>
    #include <time.h>
    #include <fcntl.h>
    
    #include "findfile.h"
    
    #ifdef HAVE_MALLOC_H
    # include <malloc.h>
    #endif
    
    #ifndef __GNUC__
    # if HAVE_ALLOCA_H
    #  include <alloca.h>
    # else
    #  ifdef _AIX
      #pragma alloca
    #  else
    #   ifndef alloca /* predefined by HP cc +Olibcalls */
    extern char *alloca ();
    #   endif
    #  endif
    # endif
    #endif
    
    slevy's avatar
    slevy committed
    
    #include <FL/Fl.H>
    
    #include "specks.h"
    #include "partiviewc.h"
    #include "partiview.H"
    
    struct sockaddr_in netsin;
    
    int netfd = -1;
    
    struct netops {
        int jumps;
        int cmds;
    };
    
    
    slevy's avatar
    slevy committed
    static struct netops listening = { 1, 1 };
    static struct netops broadcasting = { 0, 0 };
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
    slevy committed
    static int net_parse_args( struct stuff **, int argc, char *argv[], char *fname, void * );
    
    slevy's avatar
    slevy committed
    static void dorecv(int fd, void *junk);
    static int setnet(struct sockaddr_in *sinp, int ttl);
    static int sendjump( float txyz[3], float rxyz[3] );
    static int sendcmd( int argc, char *argv[] );
    static void netupdate();
    static void navtrace();
    static void cmdtrace( struct stuff **, int argc, char *argv[] );
    void aer2rxyz( float rxyz[3], const float aer[3] );
    void rxyz2aer( float aer[3], const float rxyz[3] );
    
    static int netupdates;
    
    
    slevy's avatar
    slevy committed
    extern "C" {
     void nethack_init() {
    
    slevy's avatar
    slevy committed
        parti_add_commands( net_parse_args, "net", NULL );
    
    slevy's avatar
    slevy committed
     }
    
    slevy's avatar
    slevy committed
    }
    
    
    slevy's avatar
    slevy committed
    int net_parse_args( struct stuff **, int argc, char *argv[], char *fname, void * ) {
    
    slevy's avatar
    slevy committed
    
      int i;
      struct sockaddr_in sin;
      struct hostent *hp;
      struct netops *ops;
      static char Usage[] = "net addr IPADDR/PORT | listen [[-]nav] [[-]cmd] | broadcast [[-]nav] [[-]cmd]";
    
      if(argc < 1 || strcmp(argv[0], "net"))
          return 0;
    
      if(argc <= 1) {
          msg(Usage);
          return 1;
      }
    
      switch(argv[1][0]) {
        case 'a':
    	if(argc > 2) {
    	    int port = 7490, ttl = 2;
    	    char *cp, *ep;
    	    cp = strchr(argv[2], '/');
    	    ttl = 2;
    	    if(cp) {
    		*cp = '\0';
    
    slevy's avatar
    slevy committed
    		port = strtol(cp+1, &ep, 0);
    
    slevy's avatar
    slevy committed
    		if(ep != cp && *ep == '/')
    		    ttl = strtol(ep+1, NULL, 0);
    	    }
    	    sin.sin_port = htons( port );
    	    if(inet_aton(argv[2], &sin.sin_addr)) {
    		sin.sin_family = AF_INET;
    	    } else if((hp = gethostbyname(argv[2])) != NULL) {
    		memcpy(&sin.sin_addr, hp->h_addr_list[0], sizeof(sin.sin_addr));
    		sin.sin_family = hp->h_addrtype;
    	    } else {
    
    slevy's avatar
    slevy committed
    		msg("%s: net addrs: %s: unknown host\n", fname, argv[2]);
    
    slevy's avatar
    slevy committed
    		break;
    	    }
    	    setnet( &sin, ttl );
    	}
    	msg("net addr %s/%d", inet_ntoa( netsin.sin_addr ), htons( netsin.sin_port ));
    	break;
        
        case 'l':
        case 'b':
    	ops = (argv[1][0] == 'l') ? &listening : &broadcasting;
    	for(i = 2; i < argc; i++) {
    	    char *cp = &argv[i][0];
    	    int on = 1;
    	    if(*cp == '-') on = 0, cp++;
    	    if(*cp == '+') on = 1, cp++;
    	    switch(*cp) {
    		case 'j': ops->jumps = on; break;
    		case 'c': ops->cmds = on; break;
    		case 'o':
    			  if(!strcmp(cp, "on")) ops->jumps = ops->cmds = 1;
    			  else if(!strcmp(cp, "off")) ops->jumps = ops->cmds = 0;
    			  break;
    	    }
    	}
    	msg("net %s %cjumps %ccmds",
    		argv[1][0]=='l' ? "listen" : "broadcast",
    		"-+"[ops->jumps], "-+"[ops->cmds]);
    	break;
    
        case 'j': {
    	Point xyz;
    	float aer[3], rxyz[3];
    	Matrix c2w;
    	int sendit = 0;
    	parti_getc2w( &c2w );
    	tfm2xyzaer( &xyz, aer, &c2w );
    	aer2rxyz( rxyz, aer );
    
    	if(argc > 2 && argv[2][0] == 'h') {
    	    sendit = 1;
    	} else if(argc > 5) {
    	    sendit = getfloats( &xyz.x[0], 3, 2, argc, argv );
    	    sendit += getfloats( &rxyz[0], 3, 5, argc, argv );
    	}
    	if(sendit)
    	    sendjump( &xyz.x[0], &rxyz[0] );
    	break;
          }
    
        case 'c':
    	sendcmd( argc-2, argv+2 );
    	break;
    
        default:
    	msg(Usage);
    	break;
      }
      netupdate();
      return 1;
    }
    
    static void netupdate() {
        netupdates = 0;
        if(netsin.sin_addr.s_addr == INADDR_ANY) {
    	ppui.drawtrace = NULL;
    
    	parti_cmdtrace( NULL );
    
    slevy's avatar
    slevy committed
     	return;
        }
        ppui.drawtrace = broadcasting.jumps ? navtrace : NULL;
    
        parti_cmdtrace( broadcasting.cmds ? cmdtrace : NULL );
    
    slevy's avatar
    slevy committed
    }
    
    void aer2rxyz( float rxyz[3], const float aer[3] ) {
        rxyz[0] = aer[0];
        rxyz[2] = aer[1];
        rxyz[1] = aer[2];
    }
    
    void rxyz2aer( float aer[3], const float rxyz[3] ) {
        aer[0] = rxyz[0];
        aer[1] = rxyz[2];
        aer[2] = rxyz[1];
    }
    
    static void navtrace() {
        static Matrix oldc2w;
        Matrix c2w;
    
        if(!broadcasting.jumps)
    	return;
    
        parti_getc2w( &c2w );
        if(netupdates == 0 || memcmp(&c2w, &oldc2w, sizeof(c2w)) != 0) {
    	Point pos;
    	float aer[3], rxyz[3];
    	oldc2w = c2w;
    	netupdates++;
    	tfm2xyzaer( &pos, aer, &c2w );
    	aer2rxyz( rxyz, aer );
    	sendjump( pos.x, rxyz );
        }
    }
    
    static void cmdtrace( struct stuff **, int argc, char *argv[] ) {
        if(!broadcasting.cmds)
    	return;
        if(argc>1 && !strcmp(argv[0], "net"))	/* unsafe to broadcast "net ..." cmds */
    	return;
        sendcmd( argc, argv );
    }
    
    static int setnet(struct sockaddr_in *sinp, int ttl) {
        int multi = IN_CLASSD( ntohl( sinp->sin_addr.s_addr ) );
        static int on = 1;
    
        if(netfd >= 0) {
    	Fl::remove_fd( netfd );
    	close(netfd);
    	netfd = -1;
        }
    
        netfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
        if(netfd < 0) {
    	perror("socket");
    	return -1;
        }
    
        fcntl(netfd, F_SETFL, fcntl(netfd, F_GETFL, 0) | O_NONBLOCK|O_NDELAY);
    
    
    #ifdef SO_REUSEPORT
        if(setsockopt(netfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
    	perror("setsockopt: SO_REUSEPORT");
        }
    #else /* if (as in Linux) no SO_REUSEPORT, try SO_REUSEADDR at least */
        if(setsockopt(netfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
    	perror("setsockopt: SO_REUSEADDR");
        }
    #endif /*SO_REUSEPORT*/
    
        if(bind(netfd, (struct sockaddr *)sinp, sizeof(*sinp)) < 0) {
    	struct sockaddr_in bsin = *sinp;
    	bsin.sin_addr.s_addr = INADDR_ANY;
    	if(bind(netfd, (struct sockaddr *)&bsin, sizeof(bsin)) < 0) {
    	    fprintf(stderr, "bind to %s: ", inet_ntoa(sinp->sin_addr));
    	    perror("");
    	    return -1;
    	}
        }
    
        if(multi) {
    	unsigned char mttl = ttl;
    	static int bufsize = 32768;
    	static unsigned char nope = 0;
    	struct ip_mreq mr;
    
    	if(mttl < 2) mttl = 2;
    
    	if(setsockopt(netfd, IPPROTO_IP, IP_MULTICAST_LOOP, &nope, 1) < 0) {
    	    perror("disabling IP_MULTICAST_LOOP");
    	}
    
    	if(setsockopt(netfd, IPPROTO_IP, IP_MULTICAST_TTL, &mttl, 1) < 0) {
    	    perror("setting IP_MULTICAST_TTL");
    	}
    
    	if(setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(int)) < 0) {
    	    perror("multicast SO_SNDBUF");
    	}
    
    
    	if(setsockopt(netfd, IPPROTO_IP, IP_MULTICAST_TTL, &mttl, 1) < 0) {
    	    perror("setting IP_MULTICAST_TTL");
    	}
    
    #ifdef IP_ADD_MEMBERSHIP
    	mr.imr_interface.s_addr = INADDR_ANY;
    	mr.imr_multiaddr.s_addr = sinp->sin_addr.s_addr;
    	if (setsockopt(netfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) {
    	    fprintf(stderr, "IP_ADD_MEMBERSHIP: can't join multicast group %s",
    		    inet_ntoa(sinp->sin_addr));
    	    perror("");
    	}
    #endif /*IP_ADD_MEMBERSHIP*/
    
        }
    
        netsin = *sinp;
    
        Fl::add_fd( netfd, dorecv, NULL );
        netupdate();
    
    slevy's avatar
    slevy committed
        return 1;
    
    slevy's avatar
    slevy committed
    }
    
    #define PV_MAGIC 0x19570118
    #define PV_JUMP  ('j'<<24 | 'u'<<16 | 'm'<<8 | 'p')
    #define PV_CMD   ('c'<<24 | 'm'<<16 | 'd'<<8 | 0)
    
    struct jumper {
        int magic;
        int kind;
        float txyz[3];
        float rxyz[3];
    };
    
    void htonlfloats( float *dst, float *src, int count ) {
        int i;
        for(i = 0; i < count; i++)
    	*(int *)&dst[i] = htonl( *(int *)&src[i] );
    }
    
    static int sendjump( float txyz[3], float rxyz[3] ) {
        struct jumper jump;
    
        if(netfd < 0)
    	return 0;
        jump.magic = htonl(PV_MAGIC);
        jump.kind = htonl(PV_JUMP);
        htonlfloats( jump.txyz, txyz, 3 );
        htonlfloats( &jump.rxyz[0], &rxyz[0], 3 );
        if(sendto( netfd, &jump, sizeof(jump), 0,  (struct sockaddr *)&netsin, sizeof(netsin) ) < 0) {
    	perror("sendjump: sendto");
    	return -1;
        }
        return 1;
    }
    
    static int sendcmd( int argc, char *argv[] ) {
        if(netfd < 0)
    	return 0;
        char *str = rejoinargs( 0, argc, argv );
        int len = strlen(str);
        int totlen = 3*sizeof(int) + len + 1;
        int *buf = (int *) alloca( 3*sizeof(int) + len + 1 );
        buf[0] = htonl(PV_MAGIC);
        buf[1] = htonl(PV_CMD);
        buf[2] = htonl( strlen(str) );
        memcpy( (char *)&buf[3], str, len+1 );
        if(sendto( netfd, buf, totlen, 0,  (struct sockaddr *)&netsin, sizeof(netsin) ) < 0) {
    	perror("sendcmd: sendto");
    	return -1;
        }
        return 1;
    }
    
    static void receivejump( struct jumper *jump, int len, struct sockaddr_in *srcsin ) {
        Matrix c2w;
        Point xyz;
        float aer[3], rxyz[3];
    
        if(len < sizeof(struct jumper)) {
    	msg("short net jump from %s", inet_ntoa(srcsin->sin_addr));
    	return;
        }
        if(!listening.jumps)
    	return;
        if(broadcasting.jumps) {
    	msg("Avoiding loop with %s: setting net broadcast -jump",
    			inet_ntoa(srcsin->sin_addr));
    	broadcasting.jumps = 0;
    	netupdate();
        }
        htonlfloats( &xyz.x[0], jump->txyz, 3 );
        htonlfloats( &rxyz[0], jump->rxyz, 3 );
        rxyz2aer( aer, rxyz );
        xyzaer2tfm( &c2w, &xyz, aer );
        parti_setc2w( &c2w );
    }
    
    static void receivecmd( int *buf, int len, struct sockaddr_in *srcsin ) {
        int slen = ntohl(buf[2]);
        if(3*sizeof(int) + slen > len) {
    
    slevy's avatar
    slevy committed
    	msg("short net cmd from %s (len = %d expected %d)",
    
    slevy's avatar
    slevy committed
    			inet_ntoa(srcsin->sin_addr),
    			len, 3*sizeof(int) + slen);
    	return;
        }
        if(!listening.cmds)
    	return;
        if(broadcasting.cmds) {
    	msg("Avoiding loop with %s: setting net broadcast -cmd",
    			inet_ntoa(srcsin->sin_addr));
    	broadcasting.cmds = 0;
    	netupdate();
        }
    
        char *tstr = (char *)alloca( slen+1 );
        char *argv[512];
        int argc = tokenize( (char *)&buf[3], tstr, COUNT(argv), argv, NULL );
        specks_parse_args( &ppui.st, argc, argv );
    
    slevy's avatar
    slevy committed
        parti_redraw();
    
    slevy's avatar
    slevy committed
    }
    
    
    slevy's avatar
    slevy committed
    #ifndef __BITS_SOCKET_H
    # define  socklen_t  int
    #endif
    
    
    slevy's avatar
    slevy committed
    static void dorecv(int fd, void *junk) {
        struct sockaddr_in sfrom;
    
    slevy's avatar
    slevy committed
        socklen_t sfromlen = sizeof(sfrom);
    
    slevy's avatar
    slevy committed
        int len;
        int buf[16384];
        int kind;
    
    
    slevy's avatar
    slevy committed
        for(;;) {
    	/* remember, we set this socket to non-blocking mode */
    	/* loop to receive all that's available */
    	len = recvfrom(fd, (char *)buf, sizeof(buf), 0,
    
    slevy's avatar
    slevy committed
    	    		(struct sockaddr *)&sfrom, &sfromlen);
    
    slevy's avatar
    slevy committed
    	if(len <= 0)
    	    return;
    
    slevy's avatar
    slevy committed
    
    
    slevy's avatar
    slevy committed
    	if(ntohl( buf[0] ) != PV_MAGIC) {
    	    msg("net: bad magic 0x%x", ntohl( buf[0] ));
    	    return;
    	}
    	kind = ntohl( buf[1] );
    	switch(kind) {
    	    case PV_JUMP:
    		receivejump( (struct jumper *)buf, len, &sfrom );
    		break;
    	    case PV_CMD:
    		receivecmd( buf, len, &sfrom );
    		break;
    	}
    
    slevy's avatar
    slevy committed
        }
    }
    
    slevy's avatar
    slevy committed
    #endif /*USE_NETHACK*/