<> <> DIRECTORY Convert USING [ValueToRope], GVBasics USING [Connect], GVNames USING [GetConnect], MessageWindow USING [Append], Process USING [Detach, Pause, SecondsToTicks], PupDefs USING [GetLocalPupAddress, PupAddress], Rope USING [Cat, Compare, ROPE], RPC USING [MakeKey], TankInternal, TankMaster USING[Machine, maxPlayers, Tank, Torp, WorldState, WorldStateRec], TankMasterRpcControl USING [ExportInterface, UnexportInterface], UserExec USING [GetNameAndPassword]; TankMasterImpl: CEDAR MONITOR IMPORTS Convert, GVNames, MessageWindow, PupDefs, Process, Rope,RPC, TankMasterRpcControl, UserExec EXPORTS TankInternal, TankMaster = BEGIN OPEN Rope, TankMaster; world: WorldState; names: ARRAY [0..maxPlayers) OF Rope.ROPE; <<******************************************************************>> <> <<******************************************************************>> started: BOOLEAN _ FALSE; myAddress: GVBasics.Connect; Initialize: PROCEDURE = BEGIN Octal: PROC[int: INT] RETURNS[rope: Rope.ROPE] = INLINE { RETURN[Convert.ValueToRope[[signed[int, 8]]]]}; pupAddress: PupDefs.PupAddress; TRUSTED {pupAddress _ PupDefs.GetLocalPupAddress[[0,0], NIL]}; myAddress _ Rope.Cat[Octal[pupAddress.net], "#", Octal[pupAddress.host], "#"]; world _ NEW[WorldStateRec _ []]; TRUSTED {Process.Detach[FORK WatchDog[]]}; END; WatchDog: PROCEDURE = BEGIN current: GVBasics.Connect; time: CARDINAL _ 5; WHILE TRUE DO Process.Pause[Process.SecondsToTicks[time]]; IF ~started THEN LOOP; current _ GVNames.GetConnect["TankMaster.auto"].connect; SELECT Rope.Compare[myAddress, current] FROM greater => {StopServer[]; StartServer[]; LOOP}; -- re-export the interface less => {StopServer[]; LOOP}; -- bow out to the other server ENDCASE; FOR i: CARDINAL IN [0..world.players) DO IF ~world.tank[i].playing THEN RemovePlayer[i] ELSE world.tank[i].playing _ FALSE; ENDLOOP; ENDLOOP; END; StartServer: PUBLIC ENTRY PROC = BEGIN user, password: Rope.ROPE; IF started THEN RETURN; [user, password] _ UserExec.GetNameAndPassword[]; MessageWindow.Append["Starting tank server . . .", TRUE]; TRUSTED {TankMasterRpcControl.ExportInterface[ interfaceName: [instance: "TankMaster.auto"], user: "TankMaster.auto", password: RPC.MakeKey["Tank"]]}; started _ TRUE; MessageWindow.Append["done."]; END; StopServer: PUBLIC ENTRY PROC = BEGIN IF ~started THEN RETURN; MessageWindow.Append["Stopping tank server . . .", TRUE]; TankMasterRpcControl.UnexportInterface[]; started _ FALSE; MessageWindow.Append["done."]; END; <<*****************************************************************>> <> <<******************************************************************>> AddPlayer: PUBLIC ENTRY PROC[name: Rope.ROPE, address: Machine] RETURNS[ok: BOOLEAN, id: CARDINAL] = BEGIN FOR i: CARDINAL IN [0..maxPlayers) DO IF world.tank[i].angle <= 400 THEN LOOP; world.address[i] _ address; world.tank[i] _ []; world.tank[i].angle _ 400; -- uninitialized but playing world.tank[i].playing _ TRUE; -- gives the player 10 seconds to start world.torp[i] _ []; world.score[i] _ 0; names[i] _ IF name#NIL THEN name ELSE "NoName"; SetPlayers[]; RETURN[TRUE, i]; ENDLOOP; RETURN[FALSE, maxPlayers]; -- should interpret as 'Sorry!' END; RemovePlayer: PUBLIC ENTRY PROC[id: CARDINAL] = BEGIN IF id >= maxPlayers THEN RETURN; IF world.tank[id].angle > 400 THEN RETURN; world.tank[id].angle _ 500; names[id] _ NIL; SetPlayers[]; END; GetPlayer: PUBLIC ENTRY PROC[id: CARDINAL] RETURNS[name: ROPE, address: Machine, score: INTEGER] = BEGIN IF id >= maxPlayers THEN RETURN[NIL, [[0], [0]], 0]; RETURN[names[id], world.address[id], world.score[id]]; END; SetPlayers: PROCEDURE = INLINE BEGIN world.players _ 0; FOR i: CARDINAL DECREASING IN [0..maxPlayers) DO IF world.tank[i].angle <= 400 THEN {world.players _ i+1; EXIT}; ENDLOOP; END; <<******************************************************************>> <> <<******************************************************************>> Update: PUBLIC ENTRY PROC[id: CARDINAL, tank: Tank, torp: Torp] RETURNS[return: WorldStateRec] = BEGIN IF id >= maxPlayers THEN RETURN; IF world.tank[id].angle > 400 THEN RETURN; tank.dead _ FALSE; IF ~world.tank[id].dead THEN world.tank[id] _ tank; IF id >= world.players THEN SetPlayers[]; IF world.torp[id].state # 4 OR torp.state <= 4 THEN world.torp[id] _ torp; -- don't set if it hit something. return _ world^; world.tank[id].dead _ FALSE; world.tank[id].playing _ TRUE; -- this is the "dirty" bit. END; ScoreHit: PUBLIC ENTRY PROC[torp, target: CARDINAL] = BEGIN IF torp < maxPlayers THEN { -- maxPlayers = a mine world.torp[torp].state _ 4; -- exploding world.score[torp] _ world.score[torp] + 1}; IF target < maxPlayers THEN { world.tank[target].dead _ TRUE; world.score[target] _ world.score[target] - 1}; END; Initialize[]; END...