diff --git a/polymer/eduke32/build/src/winlayer.c b/polymer/eduke32/build/src/winlayer.c
index 1b0f8407d..230269709 100644
--- a/polymer/eduke32/build/src/winlayer.c
+++ b/polymer/eduke32/build/src/winlayer.c
@@ -3403,7 +3403,9 @@ static int32_t SetupOpenGL(int32_t width, int32_t height, int32_t bitspp)
         else if (!Bstrcmp(glinfo.vendor,"3Dfx Interactive Inc.")) err = 1;
         else if (!Bstrcmp(glinfo.vendor,"Intel"))
         {
-            if (!Bstrcmp(glinfo.renderer,"Intel 865G"))
+            if (!Bstrcmp(glinfo.renderer,"Intel 845G"))
+                err = 0;
+            else if (!Bstrcmp(glinfo.renderer,"Intel 865G"))
                 err = 0;
             else if (!Bstrcmp(glinfo.renderer,"Intel 915G"))
                 err = 0;
diff --git a/polymer/eduke32/source/duke3d.h b/polymer/eduke32/source/duke3d.h
index 3a47940bb..296bbfede 100644
--- a/polymer/eduke32/source/duke3d.h
+++ b/polymer/eduke32/source/duke3d.h
@@ -832,6 +832,7 @@ enum GamevarFlags_t {
     GAMEVAR_CHARPTR    = 0x00010000, // plValues is a pointer to a char
     GAMEVAR_NORESET    = 0x00020000, // var values are not reset when restoring map state
     GAMEVAR_SPECIAL    = 0x00040000, // flag for structure member shortcut vars
+    GAMEVAR_NOMULTI    = 0x00080000, // don't attach to multiplayer packets
 };
 
 enum GamearrayFlags_t {
@@ -1094,31 +1095,32 @@ enum DukePacket_t
 {
     PACKET_MASTER_TO_SLAVE,
     PACKET_SLAVE_TO_MASTER,
-    PACKET_VERSION,
-
-    /* don't change anything above this line */
 
     PACKET_NUM_PLAYERS,
     PACKET_PLAYER_INDEX,
     PACKET_PLAYER_DISCONNECTED,
-    PACKET_MESSAGE,
-
+    PACKET_PLAYER_SPAWN,
+    PACKET_REQUEST_GAMESTATE,
     PACKET_NEW_GAME,
+    PACKET_LOAD_GAME,
+
+    // any packet with an ID higher than PACKET_BROADCAST is rebroadcast by server
+    // this is so hacked clients can't create fake server packets and get the server
+    // to send them to everyone
+
+    PACKET_BROADCAST,
     PACKET_RTS,
-    PACKET_MENU_LEVEL_QUIT,
     PACKET_WEAPON_CHOICE,
     PACKET_PLAYER_OPTIONS,
     PACKET_PLAYER_NAME,
-
-    PACKET_REQUEST_GAMESTATE,
+    PACKET_VERSION,
+    PACKET_MESSAGE,
     PACKET_USER_MAP,
 
     PACKET_MAP_VOTE,
     PACKET_MAP_VOTE_INITIATE,
     PACKET_MAP_VOTE_CANCEL,
 
-    PACKET_LOAD_GAME,
-    PACKET_NULL_PACKET,
     PACKET_PLAYER_READY,
     PACKET_QUIT = 255 // should match mmulti I think
 };
diff --git a/polymer/eduke32/source/funct.h b/polymer/eduke32/source/funct.h
index 07fc9d06e..c73e33e9b 100644
--- a/polymer/eduke32/source/funct.h
+++ b/polymer/eduke32/source/funct.h
@@ -184,6 +184,7 @@ extern void G_MoveWorld(void);
 extern void A_MoveCyclers(void);
 extern void A_MoveDummyPlayers(void);
 extern void P_ResetStatus(int32_t snum);
+extern void P_ResetPlayer(int32_t snum);
 
 // game.c
 extern inline void G_SetStatusBarScale(int32_t sc);
diff --git a/polymer/eduke32/source/game.c b/polymer/eduke32/source/game.c
index 89e8e4b02..b5d83dfb0 100644
--- a/polymer/eduke32/source/game.c
+++ b/polymer/eduke32/source/game.c
@@ -52,6 +52,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ENetHost * net_server = NULL;
 ENetHost * net_client = NULL;
 ENetPeer * net_peer = NULL;
+int32_t net_port = 23513;
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
@@ -78,7 +79,6 @@ static int32_t g_noSound = 0;
 static int32_t g_noMusic = 0;
 static char *CommandMap = NULL;
 static char *CommandName = NULL;
-static int32_t g_keepAddr = 0;
 int32_t CommandWeaponChoice = 0;
 static struct strllist
 {
@@ -608,6 +608,7 @@ void Net_Disconnect(void)
         enet_peer_reset(net_peer);
         net_peer = NULL;
         enet_host_destroy(net_client);
+        net_client = NULL;
     }
 
     if (net_server)
@@ -636,6 +637,7 @@ void Net_Disconnect(void)
             }
         }
         enet_host_destroy(net_server);
+        net_server = NULL;
     }
 }
 
@@ -794,7 +796,8 @@ void Net_NewGame(int32_t volume, int32_t level)
 }
 
 static mapstate_t *g_multiMapState = NULL;
-static int32_t spritecrc[MAXSPRITES], lastupdate[MAXSPRITES];
+static int32_t spritecrc[MAXSPRITES], lastupdate[MAXSPRITES], sectcrc[MAXSECTORS], lastsectupdate[MAXSECTORS];
+static int32_t wallcrc[MAXWALLS], lastwallupdate[MAXWALLS];
 static int32_t peractorvals[MAXGAMEVARS][MAXSPRITES], perplayervals[MAXGAMEVARS][MAXPLAYERS];
 
 void Net_ParsePacket(ENetEvent * event)
@@ -816,9 +819,9 @@ void Net_ParsePacket(ENetEvent * event)
 
             j = 1;
 
-            packbufleng = qlz_size_decompressed((char *)packbuf+1);
+            packbufleng = qlz_size_decompressed((char *)(packbuf)+1);
             packbuf = Bcalloc(1, packbufleng);
-            packbufleng = qlz_decompress((char *)event->packet->data+1, packbuf+1, state_decompress);
+            packbufleng = qlz_decompress((char *)(event->packet->data)+1, (char *)(packbuf)+1, state_decompress);
 
             Bmemcpy(&ticrandomseed, &packbuf[j], sizeof(ticrandomseed));
             j += sizeof(ticrandomseed);
@@ -827,10 +830,13 @@ void Net_ParsePacket(ENetEvent * event)
             {
                 if (g_player[i].playerquitflag == 0) continue;
 
-                if (i == myconnectindex)
+                Bmemcpy(&g_player[i].ps->dead_flag, &packbuf[j], sizeof(int16_t));
+                j += sizeof(int16_t);
+
+                if (i == myconnectindex && !g_player[i].ps->dead_flag)
                 {
-                    j += sizeof(input_t)-sizeof(loc.filler)+(sizeof(vec3_t)*3) + 4;
-                     goto process;
+                    j += sizeof(input_t)-sizeof(loc.filler)+(sizeof(vec3_t)*3) + sizeof(int16_t)*2;
+                    goto process;
                 }
 
                 nsyn = (input_t *)&inputfifo[0][0];
@@ -853,6 +859,8 @@ void Net_ParsePacket(ENetEvent * event)
 process:
                 Bmemcpy(&sprite[g_player[i].ps->i].extra, &packbuf[j], sizeof(int16_t));
                 j += sizeof(int16_t);
+                Bmemcpy(&sprite[g_player[i].ps->i].cstat, &packbuf[j], sizeof(int16_t));
+                j += sizeof(int16_t);
                 Bmemcpy(&g_player[i].ps->kickback_pic, &packbuf[j], sizeof(int16_t));
                 j += sizeof(int16_t);
                 Bmemcpy(&g_player[i].ps->shield_amount, &packbuf[j], sizeof(int16_t));
@@ -877,11 +885,22 @@ process:
                 j += sizeof(int16_t);
                 Bmemcpy(&g_player[i].ps->last_extra, &packbuf[j], sizeof(int16_t));
                 j += sizeof(int16_t);
-                Bmemcpy(&g_player[i].ps->dead_flag, &packbuf[j], sizeof(int16_t));
-                j += sizeof(int16_t);
 
                 l = i;
 
+                {
+                    int16_t i = g_player[l].ps->i, jj = j++;
+                    int32_t oa = T5-(intptr_t)&script[0];
+
+                    Bmemcpy(&T5, &packbuf[j], sizeof(T5));
+                    j += sizeof(T5);
+
+                    if (oa != T5) T3 = T4 = 0;
+                    if (packbuf[jj] & 2) T5 += (intptr_t)&script[0];
+                }
+
+                i = l;
+
                 do
                 {
                     int16_t var_id;
@@ -935,10 +954,12 @@ process:
                     deletesprite(ahead);
 
                 // sprite updates tacked on to the end of the packet
-                while ((unsigned)(packbufleng-j) > sizeof(spritetype)+sizeof(ActorData_t))
+
+                l = packbuf[j++];
+                while (l--)
                 {
-                    int16_t i, sect, statnum, osect, ostatnum, jj, lightid, opicnum;
-                    _prlight *mylight;
+                    int16_t i, sect, statnum, osect, ostatnum, jj, lightid = -1, opicnum;
+                    _prlight *mylight = NULL;
 
                     Bmemcpy(&i, &packbuf[j], sizeof(int16_t));
                     j += sizeof(int16_t);
@@ -968,16 +989,17 @@ process:
                         sprite[i].statnum = ostatnum;
                         if (sect != osect) changespritesect(i, sect);
                         if (statnum != ostatnum) changespritestat(i, statnum);
+                        mylight = ActorExtra[i].lightptr;
+                        lightid = ActorExtra[i].lightId;
                     }
 
                     j += sizeof(spritetype);
 
                     jj = j++;
 
-                    mylight = ActorExtra[i].lightptr;
-                    lightid = ActorExtra[i].lightId;
-                    Bmemcpy(&ActorExtra[i], &packbuf[j], sizeof(ActorData_t)-sizeof(ActorExtra[0].filler));
-                    j += sizeof(ActorData_t)-sizeof(ActorExtra[0].filler);
+                    Bmemcpy(&ActorExtra[i], &packbuf[j], sizeof(ActorData_t)-sizeof(ActorExtra[0].filler)-
+                        sizeof(ActorExtra[0].projectile)-sizeof(ActorExtra[0].lightptr));
+                    j += sizeof(ActorData_t)-sizeof(ActorExtra[0].filler)-sizeof(ActorExtra[0].projectile)-sizeof(ActorExtra[0].lightptr);
 
                     ActorExtra[i].projectile = &SpriteProjectile[i];
                     ActorExtra[i].lightptr = mylight;
@@ -1001,6 +1023,24 @@ process:
                     }
                     while (1);
                 }
+
+                l = packbuf[j++];
+                while (l--)
+                {
+                    Bmemcpy(&i, &packbuf[j], sizeof(int16_t));
+                    j += sizeof(int16_t);
+                    Bmemcpy(&sector[i], &packbuf[j], sizeof(sectortype));
+                    j += sizeof(sectortype);
+                }
+
+                l = packbuf[j++];
+                while (l--)
+                {
+                    Bmemcpy(&i, &packbuf[j], sizeof(int16_t));
+                    j += sizeof(int16_t);
+                    Bmemcpy(&sector[i], &packbuf[j], sizeof(walltype));
+                    j += sizeof(walltype);
+                }
             }
 
             Bfree(packbuf);
@@ -1010,9 +1050,9 @@ process:
         case PACKET_SLAVE_TO_MASTER:  //[1] (receive slave sync buffer)
             j = 1;
 
-            packbufleng = qlz_size_decompressed((char *)packbuf+1);
+            packbufleng = qlz_size_decompressed((char *)(packbuf)+1);
             packbuf = Bcalloc(1, packbufleng);
-            packbufleng = qlz_decompress((char *)event->packet->data+1, packbuf+1, state_decompress);
+            packbufleng = qlz_decompress((char *)(event->packet->data)+1, (char *)(packbuf)+1, state_decompress);
 
             nsyn = (input_t *)&inputfifo[0][0];
 
@@ -1022,6 +1062,13 @@ process:
 
             g_player[other].movefifoend++;
 
+            // anyone the server thinks is dead can go fuck themselves
+            if (g_player[other].ps->dead_flag)
+            {
+                Bfree(packbuf);
+                break;
+            }
+
             Bmemcpy(&g_player[other].ps->posx, &packbuf[j], sizeof(vec3_t) * 3);
             Bmemcpy(&sprite[g_player[other].ps->i], &packbuf[j], sizeof(vec3_t));
             sprite[g_player[other].ps->i].z += PHEIGHT;
@@ -1033,22 +1080,17 @@ process:
 
             {
                 int16_t i = g_player[other].ps->i, jj = j++;
+                int32_t oa = T5-(intptr_t)&script[0];
 
-                Bmemcpy(&T3, &packbuf[j], sizeof(T3));
-                j += sizeof(T3);
-                Bmemcpy(&T4, &packbuf[j], sizeof(T4));
-                j += sizeof(T4);
                 Bmemcpy(&T5, &packbuf[j], sizeof(T5));
                 j += sizeof(T5);
 
+                if (oa != T5) T3 = T4 = 0;
                 if (packbuf[jj] & 2) T5 += (intptr_t)&script[0];
             }
             Bfree(packbuf);
             break;
 
-        case PACKET_NULL_PACKET:
-            break;
-
         case PACKET_PLAYER_READY:
             if (net_server)
             {
@@ -1135,6 +1177,11 @@ process:
                 for (i=0; i<playerswhenstarted-1; i++) connectpoint2[i] = i+1;
                 connectpoint2[playerswhenstarted-1] = -1;
 
+                Net_SendVersion();
+                Net_SendPlayerName();
+                Net_SendPlayerOptions();
+                Net_SendWeaponChoice();
+
                 break;
 
                 // receive client player index from server
@@ -1147,7 +1194,6 @@ process:
                 Net_SendPlayerName();
                 Net_SendPlayerOptions();
                 Net_SendWeaponChoice();
-                Net_SendUserMapName();
 
                 break;
 
@@ -1157,6 +1203,10 @@ process:
                 g_player[packbuf[1]].playerquitflag = 0;
                 break;
 
+            case PACKET_PLAYER_SPAWN:
+                P_ResetPlayer(packbuf[1]);
+                break;
+
             case PACKET_PLAYER_OPTIONS:
                 other = packbuf[1];
                 i = 2;
@@ -1200,11 +1250,6 @@ process:
 
                 break;
 
-            case PACKET_MENU_LEVEL_QUIT:
-                //slaves in M/S mode only send to master
-
-                break;
-
             case PACKET_USER_MAP:
                 Bstrcpy(boardfilename,(char *)packbuf+1);
                 boardfilename[packbufleng-1] = 0;
@@ -1313,6 +1358,8 @@ process:
 
                     // a player connecting is a good time to mark everything as needing to be updated
                     Bmemset(spritecrc, 0, sizeof(spritecrc));
+                    Bmemset(sectcrc, 0, sizeof(sectcrc));
+                    Bmemset(wallcrc, 0, sizeof(wallcrc));
                     Bmemset(peractorvals, 0, sizeof(peractorvals));
                     Bmemset(perplayervals, 0, sizeof(perplayervals));
                 }
@@ -1389,22 +1436,24 @@ void Net_GetPackets(void)
 
                 if (g_player[0].ps->gm & MODE_GAME)
                 {
-                    j = g_player[i].ps->i;
-                    Bmemcpy(g_player[i].ps, g_player[0].ps, sizeof(DukePlayer_t));
-                    g_player[i].ps->i = j;
-                    changespritestat(j, STAT_PLAYER);
-                    P_ResetStatus(i);
-                    P_ResetWeapons(i);
-                    P_ResetInventory(i);
-
-                    g_player[i].ps->last_extra = g_player[i].ps->max_player_health;
-                    sprite[g_player[i].ps->i].extra = g_player[i].ps->max_player_health;
-                    g_player[i].ps->runspeed = g_playerFriction;
-
                     if (g_multiMapState == NULL) g_multiMapState = Bcalloc(1, sizeof(mapstate_t));
                     if (g_multiMapState)
                     {
                         char * buf = Bmalloc(sizeof(mapstate_t)<<1);
+
+                        j = g_player[i].ps->i;
+                        Bmemcpy(g_player[i].ps, g_player[0].ps, sizeof(DukePlayer_t));
+                        g_player[i].ps->i = j;
+                        changespritestat(j, STAT_PLAYER);
+                        P_ResetStatus(i);
+                        P_ResetWeapons(i);
+                        P_ResetInventory(i);
+
+                        g_player[i].ps->last_extra = g_player[i].ps->max_player_health;
+                        sprite[g_player[i].ps->i].extra = g_player[i].ps->max_player_health;
+                        sprite[g_player[i].ps->i].cstat = 1+256;
+                        g_player[i].ps->runspeed = g_playerFriction;
+
                         G_SaveMapState(g_multiMapState);
                         j = qlz_compress((char *)g_multiMapState, buf, sizeof(mapstate_t), state_compress);
                         while (j > 1024)
@@ -1431,7 +1480,7 @@ void Net_GetPackets(void)
                 // broadcast takes care of enet_packet_destroy itself
                 // we set the state to disconnected so enet_host_broadcast doesn't send the player back his own packets
                 // SLAVE_TO_MASTER packets are channelID 1, so they aren't broadcast anywhere
-                if (event.channelID == 0)
+                if (event.channelID == 0 && event.packet->data[0] > PACKET_BROADCAST)
                 {
                     event.peer->state = ENET_PEER_STATE_DISCONNECTED;
                     enet_host_broadcast(net_server, 0, event.packet);
@@ -1467,12 +1516,12 @@ void Net_GetPackets(void)
             switch (event.type)
             {
             case ENET_EVENT_TYPE_RECEIVE:
-/*
+
                 initprintf ("A packet of length %u was received from player %d on channel %u.\n",
                     event.packet -> dataLength,
                     event.peer -> data,
                     event.channelID);
-*/
+
                 // channelID 1 is the map state transfer from the server
                 if (event.channelID == 1)
                 {
@@ -1579,13 +1628,12 @@ void faketimerhandler(void)
 
     if (numplayers < 2)
     {
-        if (ud.multimode > 1)
+        if (ud.multimode > 1 && ud.playerai)
             TRAVERSE_CONNECT(i)
             if (i != myconnectindex)
             {
                 //clearbufbyte(&inputfifo[g_player[i].movefifoend&(MOVEFIFOSIZ-1)][i],sizeof(input_t),0L);
-                if (ud.playerai)
-                    computergetinput(i,&inputfifo[0][i]);
+                computergetinput(i,&inputfifo[0][i]);
             }
     }
 
@@ -1615,10 +1663,6 @@ void faketimerhandler(void)
             T5 -= (intptr_t)&script[0];
         }
 
-        Bmemcpy(&packbuf[j], &T3, sizeof(T3));
-        j += sizeof(T3);
-        Bmemcpy(&packbuf[j], &T4, sizeof(T4));
-        j += sizeof(T4);
         Bmemcpy(&packbuf[j], &T5, sizeof(T5));
         j += sizeof(T5);
 
@@ -1627,8 +1671,8 @@ void faketimerhandler(void)
         {
             char buf[1024];
 
-            j = qlz_compress((char *)packbuf+1, (char *)buf, j, state_compress);
-            Bmemcpy((char *)packbuf+1, (char *)buf, j);
+            j = qlz_compress((char *)(packbuf)+1, (char *)buf, j, state_compress);
+            Bmemcpy((char *)(packbuf)+1, (char *)buf, j);
             j++;
         }
 
@@ -1662,6 +1706,8 @@ void faketimerhandler(void)
 
             Bmemcpy(&osyn[i], &nsyn[i], sizeof(input_t));
 
+            Bmemcpy(&packbuf[j], &g_player[i].ps->dead_flag, sizeof(int16_t));
+            j += sizeof(int16_t);
             Bmemcpy(&packbuf[j], &nsyn[i], sizeof(input_t));
             j += sizeof(input_t)-sizeof(loc.filler);
             Bmemcpy(&packbuf[j], &g_player[i].ps->posx, sizeof(vec3_t) * 3);
@@ -1670,6 +1716,8 @@ void faketimerhandler(void)
             j += sizeof(int16_t) * 2;
             Bmemcpy(&packbuf[j], &sprite[g_player[i].ps->i].extra, sizeof(int16_t));
             j += sizeof(int16_t);
+            Bmemcpy(&packbuf[j], &sprite[g_player[i].ps->i].cstat, sizeof(int16_t));
+            j += sizeof(int16_t);
             Bmemcpy(&packbuf[j], &g_player[i].ps->kickback_pic, sizeof(int16_t));
             j += sizeof(int16_t);
             Bmemcpy(&packbuf[j], &g_player[i].ps->shield_amount, sizeof(int16_t));
@@ -1694,16 +1742,39 @@ void faketimerhandler(void)
             j += sizeof(int16_t);
             Bmemcpy(&packbuf[j], &g_player[i].ps->last_extra, sizeof(int16_t));
             j += sizeof(int16_t);
-            Bmemcpy(&packbuf[j], &g_player[i].ps->dead_flag, sizeof(int16_t));
-            j += sizeof(int16_t);
 
             l = i;
+
+            {
+                int32_t jj, oa;
+
+                i = g_player[l].ps->i;
+
+                packbuf[(jj = j++)] = 0;
+
+                if (T5 >= (intptr_t)&script[0] && T5 < (intptr_t)(&script[g_scriptSize]))
+                {
+                    packbuf[jj] |= 2;
+                    T5 -= (intptr_t)&script[0];
+                }
+
+                oa = T5;
+
+                Bmemcpy(&packbuf[j], &T5, sizeof(T5));
+                j += sizeof(T5);
+
+                if (oa != T5) T3 = T4 = 0;
+
+                if (packbuf[jj] & 2) T5 += (intptr_t)&script[0];
+            }
+
+            i = l;
             {
                 int16_t ii=g_gameVarCount-1, kk = 0;
 
                 for (; ii>=0; ii--)
                 {
-                    if ((aGameVars[ii].dwFlags & GAMEVAR_PERACTOR) && aGameVars[ii].val.plValues)
+                    if ((aGameVars[ii].dwFlags & (GAMEVAR_PERACTOR|GAMEVAR_NOMULTI)) == GAMEVAR_PERACTOR && aGameVars[ii].val.plValues)
                     {
                         if (peractorvals[ii][i] != aGameVars[ii].val.plValues[i])
                         {
@@ -1729,7 +1800,7 @@ void faketimerhandler(void)
 
                 for (; ii>=0; ii--)
                 {
-                    if ((aGameVars[ii].dwFlags & GAMEVAR_PERPLAYER) && aGameVars[ii].val.plValues)
+                    if ((aGameVars[ii].dwFlags & (GAMEVAR_PERPLAYER|GAMEVAR_NOMULTI)) == GAMEVAR_PERPLAYER && aGameVars[ii].val.plValues)
                     {
                         if (perplayervals[ii][i] != aGameVars[ii].val.plValues[i])
                         {
@@ -1763,6 +1834,9 @@ void faketimerhandler(void)
 
         {
             int32_t lists[] = { STAT_STANDABLE, STAT_EFFECTOR, STAT_ACTOR, STAT_ZOMBIEACTOR, STAT_PROJECTILE }, zz;
+            int32_t zj = j++;
+
+            packbuf[zj] = 0;
 
             for (zz = 0; (unsigned)zz < (sizeof(lists)/sizeof(lists[0])); zz++)
             TRAVERSE_SPRITE_STAT(headspritestat[lists[zz]], i, nexti)
@@ -1799,8 +1873,9 @@ void faketimerhandler(void)
                             packbuf[jj] |= 4;
                             T6 -= (intptr_t)&script[0];
                         }
-                        Bmemcpy(&packbuf[j], &ActorExtra[i], sizeof(ActorData_t)-sizeof(ActorExtra[0].filler));
-                        j += sizeof(ActorData_t)-sizeof(ActorExtra[0].filler);
+                        Bmemcpy(&packbuf[j], &ActorExtra[i], sizeof(ActorData_t)-sizeof(ActorExtra[0].filler)-
+                            sizeof(ActorExtra[0].projectile)-sizeof(ActorExtra[0].lightptr));
+                        j += sizeof(ActorData_t)-sizeof(ActorExtra[0].filler)-sizeof(ActorExtra[0].projectile)-sizeof(ActorExtra[0].lightptr);
 
                         if (packbuf[jj] & 1) T2 += (intptr_t)&script[0];
                         if (packbuf[jj] & 2) T5 += (intptr_t)&script[0];
@@ -1836,13 +1911,59 @@ void faketimerhandler(void)
                 }
                 if (k > 4) break;
             }
+            packbuf[zj] = k;
+
+            packbuf[(zj = j++)] = 0;
+            for (i = numsectors-1; i >= 0; i--)
+            {
+                if (totalclock > (lastsectupdate[i] + (TICSPERFRAME * 12)))
+                {
+                    l = crc32once((uint8_t *)&sector[i], sizeof(sectortype));
+
+                    if (sectcrc[i] != l)
+                    {
+                        sectcrc[i] = l;
+                        lastsectupdate[i] = totalclock;
+                        Bmemcpy(&packbuf[j], &i, sizeof(int16_t));
+                        j += sizeof(int16_t);
+                        Bmemcpy(&packbuf[j], &sector[i], sizeof(sectortype));
+                        j += sizeof(sectortype);
+                        k++;
+                    }
+                }
+                if (k > 6) break;
+            }
+            packbuf[zj] = k;
+
+            packbuf[(zj = j++)] = 0;
+            for (i = numwalls-1; i >= 0; i--)
+            {
+                if (totalclock > (lastwallupdate[i] + (TICSPERFRAME * 12)))
+                {
+                    l = crc32once((uint8_t *)&wall[i], sizeof(walltype));
+
+                    if (wallcrc[i] != l)
+                    {
+                        wallcrc[i] = l;
+                        lastwallupdate[i] = totalclock;
+                        Bmemcpy(&packbuf[j], &i, sizeof(int16_t));
+                        j += sizeof(int16_t);
+                        Bmemcpy(&packbuf[j], &wall[i], sizeof(walltype));
+                        j += sizeof(walltype);
+                        k++;
+                    }
+                }
+                if (k > 6) break;
+            }
+            packbuf[zj] = k;
+
         }
 
         {
             char buf[4096];
 
-            j = qlz_compress((char *)packbuf+1, (char *)buf, j, state_compress);
-            Bmemcpy((char *)packbuf+1, (char *)buf, j);
+            j = qlz_compress((char *)(packbuf)+1, (char *)buf, j, state_compress);
+            Bmemcpy((char *)(packbuf)+1, (char *)buf, j);
             j++;
         }
 
@@ -5313,7 +5434,7 @@ int32_t A_Spawn(int32_t j, int32_t pn)
             {
                 sp->xrepeat = 31;
                 sp->yrepeat = 1;
-                sp->z = sector[sprite[j].sectnum].floorz-(40<<8);
+                sp->z = sector[sprite[j].sectnum].floorz-PHEIGHT;
             }
             else
             {
@@ -7183,8 +7304,10 @@ void G_DoSpriteAnimations(int32_t x,int32_t y,int32_t a,int32_t smoothratio)
         {
             t->x -= mulscale16(65536-smoothratio,g_player[s->yvel].ps->posx-g_player[s->yvel].ps->oposx);
             t->y -= mulscale16(65536-smoothratio,g_player[s->yvel].ps->posy-g_player[s->yvel].ps->oposy);
-            t->z += /*g_player[s->yvel].ps->oposz +*/ mulscale16(smoothratio,g_player[s->yvel].ps->posz-g_player[s->yvel].ps->oposz) - PHEIGHT;
-            t->z += (40<<8);
+            // dirty hack
+            if (g_player[s->yvel].ps->dead_flag) t->z = g_player[s->yvel].ps->oposz;
+            t->z += mulscale16(smoothratio,g_player[s->yvel].ps->posz-g_player[s->yvel].ps->oposz) -
+                (g_player[s->yvel].ps->dead_flag ? 0 : PHEIGHT) + PHEIGHT;
         }
         else if ((s->statnum == STAT_DEFAULT && s->picnum != CRANEPOLE) || s->statnum == STAT_PLAYER ||
             s->statnum == STAT_STANDABLE || s->statnum == STAT_PROJECTILE || s->statnum == STAT_MISC || s->statnum == STAT_ACTOR)
@@ -7353,7 +7476,7 @@ void G_DoSpriteAnimations(int32_t x,int32_t y,int32_t a,int32_t smoothratio)
                                 {
                                     t->x = omy.x+mulscale16((int32_t)(my.x-omy.x),smoothratio);
                                     t->y = omy.y+mulscale16((int32_t)(my.y-omy.y),smoothratio);
-                                    t->z = omy.z+mulscale16((int32_t)(my.z-omy.z),smoothratio)+(40<<8);
+                                    t->z = omy.z+mulscale16((int32_t)(my.z-omy.z),smoothratio)+PHEIGHT;
                                     t->ang = omyang+mulscale16((int32_t)(((myang+1024-omyang)&2047)-1024),smoothratio);
                                     t->sectnum = mycursectnum;
                                 }
@@ -9196,7 +9319,6 @@ static void G_ShowParameterHelp(void)
               "-map [file.map]\tLoads a map\n"
               "-m\t\tDisable monsters\n"
               "-nam/-ww2gi\tRun in NAM or WW2GI-compatible mode\n"
-              "-net\t\tEnable multiplayer (see documentation)\n"
               "-r\t\tRecord demo\n"
               "-s#\t\tSet skill level (1-4)\n"
 #if defined RENDERTYPEWIN || (defined RENDERTYPESDL && !defined __APPLE__ && defined HAVE_GTK2)
@@ -9237,9 +9359,6 @@ static void G_ShowDebugHelp(void)
               "-nologo\t\tSkip the logo anim\n"
               "-ns/-nm\t\tDisable sound or music\n"
               "-q#\t\tFake multiplayer with # (2-8) players\n"
-              "-rmnet\t\tUse network config file (OBSOLETE, see -net)\n"
-              "-stun\t\tUse UDP hole punching for multiplayer connections\n"
-              /*"-unstable   \tForce EDuke32 to execute unsafe CON commands (and crash)\n"*/
               "-w\t\tShow coordinates\n"
               "-z#/-condebug\tEnable line-by-line CON compile debugging at level #\n"
               ;
@@ -9756,15 +9875,13 @@ static void G_CheckCommandLine(int32_t argc, const char **argv)
                     i++;
                     continue;
                 }
-                if (!Bstrcasecmp(c+1,"keepaddr"))
+                if (!Bstrcasecmp(c+1,"port"))
                 {
-                    g_keepAddr = 1;
-                    i++;
-                    continue;
-                }
-                if (!Bstrcasecmp(c+1,"stun"))
-                {
-                    natfree = 1; //Addfaz NatFree
+                    if (argc > i+1)
+                    {
+                        net_port = atoi(argv[i+1]);
+                        i++;
+                    }
                     i++;
                     continue;
                 }
@@ -9777,7 +9894,7 @@ static void G_CheckCommandLine(int32_t argc, const char **argv)
                     /* enet_address_set_host (& address, "x.x.x.x"); */
 
                     address.host = ENET_HOST_ANY;
-                    address.port = 23513;
+                    address.port = net_port;
 
                     net_server = enet_host_create (&address, MAXPLAYERS, 0, 0);
 
@@ -9794,6 +9911,7 @@ static void G_CheckCommandLine(int32_t argc, const char **argv)
                     {
                         ENetAddress address;
                         ENetEvent event;
+                        char * addrstr = NULL;
 
                         Net_Disconnect();
 
@@ -9806,8 +9924,9 @@ static void G_CheckCommandLine(int32_t argc, const char **argv)
                             continue;
                         }
 
-                        enet_address_set_host (&address, (char *)argv[i+1]);
-                        address.port = 23513;
+                        addrstr = strtok((char *)argv[i+1],":");
+                        enet_address_set_host (&address, addrstr);
+                        address.port = atoi((addrstr = strtok(NULL,":")) == NULL ? "23513" : addrstr);
 
                         // use 2 channels for easy packet sorting at a lower level than the game later
                         net_peer = enet_host_connect (net_client, &address, 2);    
diff --git a/polymer/eduke32/source/gameexec.c b/polymer/eduke32/source/gameexec.c
index 7079f6bcd..6674375c6 100644
--- a/polymer/eduke32/source/gameexec.c
+++ b/polymer/eduke32/source/gameexec.c
@@ -2770,68 +2770,22 @@ nullquote:
             }
             else
             {
-                vec3_t tmpvect;
+                if (vm.g_p == myconnectindex)
+                {
+                    g_cameraDistance = 0;
+                    g_cameraClock = totalclock;
+                }
 
-                tmpvect.x = g_player[vm.g_p].ps->posx;
-                tmpvect.y = g_player[vm.g_p].ps->posy;
-                tmpvect.z = g_player[vm.g_p].ps->posz+PHEIGHT;
-                P_RandomSpawnPoint(vm.g_p);
-                vm.g_sp->x = ActorExtra[vm.g_i].bposx = g_player[vm.g_p].ps->bobposx = g_player[vm.g_p].ps->oposx = g_player[vm.g_p].ps->posx;
-                vm.g_sp->y = ActorExtra[vm.g_i].bposy = g_player[vm.g_p].ps->bobposy = g_player[vm.g_p].ps->oposy =g_player[vm.g_p].ps->posy;
-                vm.g_sp->z = ActorExtra[vm.g_i].bposy = g_player[vm.g_p].ps->oposz =g_player[vm.g_p].ps->posz;
-                updatesector(g_player[vm.g_p].ps->posx,g_player[vm.g_p].ps->posy,&g_player[vm.g_p].ps->cursectnum);
-                setsprite(g_player[vm.g_p].ps->i,&tmpvect);
-                vm.g_sp->cstat = 257;
+                if (net_server)
+                {
+                    P_ResetPlayer(vm.g_p);
 
-                vm.g_sp->shade = -12;
-                vm.g_sp->clipdist = 64;
-                vm.g_sp->xrepeat = 42;
-                vm.g_sp->yrepeat = 36;
-                vm.g_sp->owner = vm.g_i;
-                vm.g_sp->xoffset = 0;
-                vm.g_sp->pal = g_player[vm.g_p].ps->palookup;
+                    packbuf[0] = PACKET_PLAYER_SPAWN;
+                    packbuf[1] = vm.g_p;
+                    packbuf[2] = 0;
 
-                g_player[vm.g_p].ps->last_extra = vm.g_sp->extra = g_player[vm.g_p].ps->max_player_health;
-                g_player[vm.g_p].ps->wantweaponfire = -1;
-                g_player[vm.g_p].ps->horiz = 100;
-                g_player[vm.g_p].ps->on_crane = -1;
-                g_player[vm.g_p].ps->frag_ps = vm.g_p;
-                g_player[vm.g_p].ps->horizoff = 0;
-                g_player[vm.g_p].ps->opyoff = 0;
-                g_player[vm.g_p].ps->wackedbyactor = -1;
-                g_player[vm.g_p].ps->shield_amount = g_startArmorAmount;
-                g_player[vm.g_p].ps->dead_flag = 0;
-                g_player[vm.g_p].ps->pals_time = 0;
-                g_player[vm.g_p].ps->footprintcount = 0;
-                g_player[vm.g_p].ps->weapreccnt = 0;
-                g_player[vm.g_p].ps->fta = 0;
-                g_player[vm.g_p].ps->ftq = 0;
-                g_player[vm.g_p].ps->posxv = g_player[vm.g_p].ps->posyv = 0;
-                g_player[vm.g_p].ps->rotscrnang = 0;
-                g_player[vm.g_p].ps->runspeed = g_playerFriction;
-                g_player[vm.g_p].ps->falling_counter = 0;
-
-                ActorExtra[vm.g_i].extra = -1;
-                ActorExtra[vm.g_i].owner = vm.g_i;
-
-                ActorExtra[vm.g_i].cgg = 0;
-                ActorExtra[vm.g_i].movflag = 0;
-                ActorExtra[vm.g_i].tempang = 0;
-                ActorExtra[vm.g_i].actorstayput = -1;
-                ActorExtra[vm.g_i].dispicnum = 0;
-                ActorExtra[vm.g_i].owner = g_player[vm.g_p].ps->i;
-
-                P_ResetInventory(vm.g_p);
-                P_ResetWeapons(vm.g_p);
-
-                g_player[vm.g_p].ps->reloading = 0;
-
-                g_player[vm.g_p].ps->movement_lock = 0;
-
-                if (apScriptGameEvent[EVENT_RESETPLAYER])
-                    X_OnEvent(EVENT_RESETPLAYER, g_player[vm.g_p].ps->i, vm.g_p, -1);
-                g_cameraDistance = 0;
-                g_cameraClock = totalclock;
+                    enet_host_broadcast(net_server, 0 , enet_packet_create(packbuf, 3, ENET_PACKET_FLAG_RELIABLE));
+                }
             }
             P_UpdateScreenPal(g_player[vm.g_p].ps);
             //AddLog("EOF: resetplayer");
diff --git a/polymer/eduke32/source/player.c b/polymer/eduke32/source/player.c
index 1a7210860..51085d77e 100644
--- a/polymer/eduke32/source/player.c
+++ b/polymer/eduke32/source/player.c
@@ -187,7 +187,7 @@ int32_t A_GetHitscanRange(int32_t i)
     int32_t zoff = 0;
     hitdata_t hitinfo;
 
-    if (PN == APLAYER) zoff = (40<<8);
+    if (PN == APLAYER) zoff = PHEIGHT;
 
     SZ -= zoff;
     hitscan((const vec3_t *)&sprite[i],SECT,
diff --git a/polymer/eduke32/source/premap.c b/polymer/eduke32/source/premap.c
index e4d25296b..0ded2afc5 100644
--- a/polymer/eduke32/source/premap.c
+++ b/polymer/eduke32/source/premap.c
@@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "duke3d.h"
 #include "osd.h"
+#include "gamedef.h"
 
 #ifdef RENDERTYPEWIN
 #define WIN32_LEAN_AND_MEAN
@@ -619,6 +620,74 @@ void P_RandomSpawnPoint(int32_t snum)
     p->oposz = p->posz = g_playerSpawnPoints[i].oz;
     p->ang = g_playerSpawnPoints[i].oa;
     p->cursectnum = g_playerSpawnPoints[i].os;
+    sprite[p->i].cstat = 1+256;
+}
+
+void P_ResetPlayer(int32_t snum)
+{
+    vec3_t tmpvect;
+    spritetype *sp = &sprite[g_player[snum].ps->i];
+
+    tmpvect.x = g_player[snum].ps->posx;
+    tmpvect.y = g_player[snum].ps->posy;
+    tmpvect.z = g_player[snum].ps->posz+PHEIGHT;
+    P_RandomSpawnPoint(snum);
+    sp->x = ActorExtra[g_player[snum].ps->i].bposx = g_player[snum].ps->bobposx = g_player[snum].ps->oposx = g_player[snum].ps->posx;
+    sp->y = ActorExtra[g_player[snum].ps->i].bposy = g_player[snum].ps->bobposy = g_player[snum].ps->oposy =g_player[snum].ps->posy;
+    sp->z = ActorExtra[g_player[snum].ps->i].bposy = g_player[snum].ps->oposz =g_player[snum].ps->posz;
+    updatesector(g_player[snum].ps->posx,g_player[snum].ps->posy,&g_player[snum].ps->cursectnum);
+    setsprite(g_player[snum].ps->i,&tmpvect);
+    sp->cstat = 257;
+
+    sp->shade = -12;
+    sp->clipdist = 64;
+    sp->xrepeat = 42;
+    sp->yrepeat = 36;
+    sp->owner = g_player[snum].ps->i;
+    sp->xoffset = 0;
+    sp->pal = g_player[snum].ps->palookup;
+
+    g_player[snum].ps->last_extra = sp->extra = g_player[snum].ps->max_player_health;
+    g_player[snum].ps->wantweaponfire = -1;
+    g_player[snum].ps->horiz = 100;
+    g_player[snum].ps->on_crane = -1;
+    g_player[snum].ps->frag_ps = snum;
+    g_player[snum].ps->horizoff = 0;
+    g_player[snum].ps->opyoff = 0;
+    g_player[snum].ps->wackedbyactor = -1;
+    g_player[snum].ps->shield_amount = g_startArmorAmount;
+    g_player[snum].ps->dead_flag = 0;
+    g_player[snum].ps->pals_time = 0;
+    g_player[snum].ps->footprintcount = 0;
+    g_player[snum].ps->weapreccnt = 0;
+    g_player[snum].ps->fta = 0;
+    g_player[snum].ps->ftq = 0;
+    g_player[snum].ps->posxv = g_player[snum].ps->posyv = 0;
+    g_player[snum].ps->rotscrnang = 0;
+    g_player[snum].ps->runspeed = g_playerFriction;
+    g_player[snum].ps->falling_counter = 0;
+
+    ActorExtra[g_player[snum].ps->i].extra = -1;
+    ActorExtra[g_player[snum].ps->i].owner = g_player[snum].ps->i;
+
+    ActorExtra[g_player[snum].ps->i].cgg = 0;
+    ActorExtra[g_player[snum].ps->i].movflag = 0;
+    ActorExtra[g_player[snum].ps->i].tempang = 0;
+    ActorExtra[g_player[snum].ps->i].actorstayput = -1;
+    ActorExtra[g_player[snum].ps->i].dispicnum = 0;
+    ActorExtra[g_player[snum].ps->i].owner = g_player[snum].ps->i;
+
+    ActorExtra[g_player[snum].ps->i].temp_data[4] = 0;
+
+    P_ResetInventory(snum);
+    P_ResetWeapons(snum);
+
+    g_player[snum].ps->reloading = 0;
+
+    g_player[snum].ps->movement_lock = 0;
+
+    if (apScriptGameEvent[EVENT_RESETPLAYER])
+        X_OnEvent(EVENT_RESETPLAYER, g_player[snum].ps->i, snum, -1);
 }
 
 void P_ResetStatus(int32_t snum)