note
	description: "[
		d_net.c
		DOOM Network game communication and protocol,
		all OS independend parts.
	]"
	license: "[
		Copyright (C) 1993-1996 by id Software, Inc.
		Copyright (C) 2005-2014 Simon Howard
		Copyright (C) 2021 Ilgiz Mustafin
		
		This program is free software; you can redistribute it and/or modify
		it under the terms of the GNU General Public License as published by
		the Free Software Foundation; either version 2 of the License, or
		(at your option) any later version.
		
		This program is distributed in the hope that it will be useful,
		but WITHOUT ANY WARRANTY; without even the implied warranty of
		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		GNU General Public License for more details.
		
		You should have received a copy of the GNU General Public License along
		with this program; if not, write to the Free Software Foundation, Inc.,
		51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
	]"

class 
	D_NET

create 
	make

feature 

	i_main: I_MAIN

	make (a_i_main: like i_main)
		do
			i_main := a_i_main
			create netbuffer.make
			create doomcom.make
			create localcmds.make_filled (create {TICCMD_T}, 0, Backuptics - 1)
			create nodeforplayer.make_filled (0, 0, {DOOMDEF_H}.maxplayers - 1)
			create frameskip.make_filled (False, 0, 3)
			create frametics.make_filled (0, 0, 3)
		end
	
feature 

	skiptics: INTEGER_32

	localcmds: ARRAY [TICCMD_T]

	nodeforplayer: ARRAY [INTEGER_32]
	
feature 

	Netcmds: ARRAY [ARRAY [TICCMD_T]]
		local
			i: INTEGER_32
			j: INTEGER_32
		once
			create Result.make_filled (create {ARRAY [TICCMD_T]}.make_empty, 0, {DOOMDEF_H}.maxplayers - 1)
			from
				i := 0
			until
				i >= {DOOMDEF_H}.maxplayers
			loop
				Result [i] := create {ARRAY [TICCMD_T]}.make_filled (create {TICCMD_T}, 0, Backuptics - 1)
				from
					j := 0
				until
					j >= Backuptics - 1
				loop
					Result [i] [j] := create {TICCMD_T}
					j := j + 1
				end
				i := i + 1
			end
		end
	
feature 

	Nettics: ARRAY [INTEGER_32]
		once
			create Result.make_filled (0, 0, Maxnetnodes - 1)
		end

	Nodeingame: ARRAY [BOOLEAN]
			-- set false as nodes leave game
		once
			create Result.make_filled (False, 0, Maxnetnodes - 1)
		end

	Remoteresend: ARRAY [BOOLEAN]
			-- set when local needs tics
		once
			create Result.make_filled (False, 0, Maxnetnodes - 1)
		end

	Resendto: ARRAY [INTEGER_32]
			-- set when remote needs tics
		once
			create Result.make_filled (0, 0, Maxnetnodes - 1)
		end
	
feature 

	d_checknetgame
		local
			i: INTEGER_32
		do
			from
				i := 0
			until
				i >= Maxnetnodes
			loop
				Nodeingame [i] := False
				Nettics [i] := 0
				Remoteresend [i] := False
				Resendto [i] := 0
				i := i + 1
			end
			check
					attached i_main.I_net as i_net
			then
				i_net.i_initnetwork
			end
			if doomcom.id /= Doomcom_id then
				i_main.i_error ("Doomcom buffer invalid!")
			end
			netbuffer := doomcom.data
			i_main.G_game.consoleplayer := doomcom.consoleplayer
			i_main.G_game.displayplayer := doomcom.consoleplayer
			if i_main.G_game.netgame then
				d_arbitratenetstart
			end
			check
					attached i_main.D_main as d
			then
				print ("startskill " + d.startskill.out + " deathmatch: " + i_main.G_game.deathmatch.out + " startmap: " + d.startmap.out + " startepisode: " + d.startepisode.out + "%N")
			end
			ticdup := doomcom.ticdup
			maxsend := Backuptics \\ (2 * ticdup) - 1
			if maxsend < 1 then
				maxsend := 1
			end
			from
				i := 0
			until
				i >= doomcom.numplayers
			loop
				i_main.G_game.Playeringame [i] := True
				i := i + 1
			end
			from
				i := 0
			until
				i >= doomcom.numnodes
			loop
				Nodeingame [i] := True
				i := i + 1
			end
			print ("player " + (i_main.G_game.consoleplayer + 1).out + " of " + doomcom.numplayers.out + " (" + doomcom.numnodes.out + " nodes)%N")
		end
	
feature 

	maketic: INTEGER_32 assign set_maketic

	Backuptics: INTEGER_32 = 12

	Maxnetnodes: INTEGER_32 = 8
			-- Max computers/players in a game.

	ticdup: INTEGER_32

	maxsend: INTEGER_32
			-- BACKUPTICS/(2*ticdup)-1
	
feature 

	doomcom: DOOMCOM_T assign set_doomcom

	Doomcom_id: INTEGER_32 = 305419896

	netbuffer: DOOMDATA_T
	
feature 

	set_doomcom (a_doomcom: like doomcom)
		do
			doomcom := a_doomcom
		end
	
feature 

	set_maketic (a_maketic: like maketic)
		do
			maketic := a_maketic
		end
	
feature -- TryRunTics

	oldentertics: INTEGER_32

	frametics: ARRAY [INTEGER_32]

	frameon: INTEGER_32

	frameskip: ARRAY [BOOLEAN]

	oldnettics: INTEGER_32

	tryruntics
		local
			i: INTEGER_32
			lowtic: INTEGER_32
			entertic: INTEGER_32
			realtics: INTEGER_32
			availabletics: INTEGER_32
			counts: INTEGER_32
			numplaying: INTEGER_32
			returned: BOOLEAN
			cmd: TICCMD_T
			buf: INTEGER_32
			j: INTEGER_32
		do
			entertic := i_main.I_system.i_gettime // ticdup
			realtics := entertic - oldentertics
			oldentertics := entertic
			netupdate
			lowtic := {DOOMTYPE_H}.maxint
			numplaying := 0
			from
				i := 0
			until
				i >= doomcom.numnodes
			loop
				if Nodeingame [i] then
					numplaying := numplaying + 1
					if Nettics [i] < lowtic then
						lowtic := Nettics [i]
					end
				end
				i := i + 1
			end
			availabletics := lowtic - i_main.G_game.gametic // ticdup
			if realtics < availabletics - 1 then
				counts := realtics + 1
			elseif realtics < availabletics then
				counts := realtics
			else
				counts := availabletics
			end
			if counts < 1 then
				counts := 1
			end
			frameon := frameon + 1
			if i_main.D_main.debugfile /= Void then
				{NOT_IMPLEMENTED}.not_implemented ("debug file writing", True)
			end
			if not i_main.G_game.demoplayback then
				from
					i := 0
				until
					i >= {DOOMDEF_H}.maxplayers or else i_main.G_game.Playeringame [i]
				loop
					i := i + 1
				end
				if i_main.G_game.consoleplayer = i then
				else
					if Nettics [0] <= Nettics [nodeforplayer [i]] then
						gametime := gametime - 1
					end
					frameskip [frameon & 3] := oldnettics > Nettics [nodeforplayer [i]]
					oldnettics := Nettics [0]
					if frameskip [0] and frameskip [1] and frameskip [2] and frameskip [3] then
						skiptics := 1
					end
				end
			end
			from
			until
				lowtic >= i_main.G_game.gametic // ticdup + counts
			loop
				netupdate
				lowtic := {DOOMTYPE_H}.maxint
				from
					i := 0
				until
					returned or i >= doomcom.numnodes
				loop
					if Nodeingame [i] and Nettics [i] < lowtic then
						lowtic := Nettics [i]
						i := i + 1
					end
				end
				if lowtic < i_main.G_game.gametic // ticdup then
					{I_MAIN}.i_error ("TryRunTics: lowtic < gametic")
				end
				if i_main.I_system.i_gettime // ticdup - entertic >= 20 then
					i_main.M_menu.m_ticker
					returned := True
				end
			end
			from
			until
				counts = 0
			loop
				counts := counts - 1
				from
					i := 0
				until
					i >= ticdup
				loop
					if i_main.G_game.gametic // ticdup > lowtic then
						{I_MAIN}.i_error ("gametic > lowtic")
					end
					if i_main.D_main.advancedemo then
						i_main.D_main.d_doadvancedemo
					end
					i_main.M_menu.m_ticker
					i_main.G_game.g_ticker
					i_main.G_game.gametic := i_main.G_game.gametic + 1
					if i /= ticdup - 1 then
						buf := (i_main.G_game.gametic // ticdup) \\ Backuptics
						from
							j := 0
						until
							j >= {DOOMDEF_H}.maxplayers
						loop
							cmd := Netcmds [j] [buf]
							cmd.chatchar := (0).to_character_8
							if cmd.buttons & {D_EVENT}.bt_special /= 0 then
								cmd.buttons := 0
							end
							j := j + 1
						end
					end
					i := i + 1
				end
			end
			netupdate
		end
	
feature 

	d_arbitratenetstart
		do
			{NOT_IMPLEMENTED}.not_implemented ("D_ArbitrateNetStart", False)
		end
	
feature -- NetUpdate

	gametime: INTEGER_32

	netupdate
			-- Builds ticcmds for console player,
			-- sends out a packet
		local
			nowtime: INTEGER_32
			newtics: INTEGER_32
			i, j: INTEGER_32
			realstart: INTEGER_32
			gameticdiv: INTEGER_32
			break: BOOLEAN
		do
			nowtime := i_main.I_system.i_gettime // ticdup
			newtics := nowtime - gametime
			gametime := nowtime
			if newtics > 0 then
				if skiptics <= newtics then
					newtics := newtics - skiptics
					skiptics := 0
				else
					skiptics := skiptics - newtics
					newtics := 0
				end
				netbuffer.player := (i_main.G_game.consoleplayer).to_natural_8
				gameticdiv := i_main.G_game.gametic // ticdup
				from
					i := 0
				until
					break or i >= newtics
				loop
					i_main.I_video.i_starttic
					check
							attached i_main.D_main as main
					then
						main.d_processevents
					end
					if maketic - gameticdiv >= Backuptics // 2 - 1 then
						break := True
					else
						i_main.G_game.g_buildticcmd (localcmds [maketic \\ Backuptics])
						maketic := maketic + 1
						i := i + 1
					end
				end
				check
						attached i_main.D_main as main
				then
					if main.singletics then
					else
						i_main.i_error ("Non-singletics NetUpdate not imlemented")
					end
				end
			else
				check
						attached i_main.D_main as main
				then
					if not main.singletics then
						getpackets
					end
				end
			end
		end

	getpackets
		do
			{NOT_IMPLEMENTED}.not_implemented ("GetPackets", False)
		end
	
invariant
		localcmds.count = Backuptics
		localcmds.lower = 0
		frametics.lower = 0 and frametics.count = 4
		frameskip.lower = 0 and frameskip.count = 4
		nodeforplayer.lower = 0 and nodeforplayer.count = {DOOMDEF_H}.maxplayers

end -- class D_NET

Generated by ISE EiffelStudio