TankMasterImpl.mesa
last modified by: John Maxwell on: February 7, 1983 10:03 am
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;
******************************************************************
Initialization
******************************************************************
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;
*****************************************************************
Adding and removing players
******************************************************************
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;
******************************************************************
Playing
******************************************************************
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...