lhk
lhk

Reputation: 30256

Typescript: can't call function from another module

My current project is a simple multiplayer game. Both client and server are written in Typescript. The client only processes the input and renders the game, all logic is implemented on the serverside.

The server code is executed with nodejs and structured like this

The game runs like this:

  1. A person clicks inside the html-canvas
  2. A new "REQUESTSPAWN" message is packaged and sent to the server
  3. A Player instance receives the message and calls requestSpawn on its instance of Game
  4. The Game instance creates a new GameObject of the correct type at the correct position and adds it to the list of GameObjects
  5. This new GameObject should now be updated.

Which it isn't. Here's the relevant code:

game.ts

import go = module("GameObjects"); 
import util = module("Utilities");

//...

export class Game {
    private players: Player[];
    public gameObjects:go.GameObject[];
    private gameObjectCounter: number;

    constructor() {
        this.players = [];
        this.gameObjectCounter = 0;
        this.gameObjects = [];
        var prev = Date.now();
        var deltaTime = Date.now() - prev;
        setInterval(() =>{ deltaTime = Date.now() - prev; prev = Date.now(); this.update(deltaTime); }, 200);
    }

    broadcast(msg: Message) {
        this.players.forEach((player) => { player.send(msg); });
    }

    requestSpawn(msg:RequestSpawnMessage, clientID:number): bool {
        var pos = new util.Vector2(msg.x, msg.y);        
        var o: go.GameObject;
        switch (msg.tag) {
            case UID.FACTORY:                                        
                o = new go.Factory(pos.clone(), this.players[clientID], this.newGameObject());
            case UID.ROBOT:
                o = new go.Robot(pos.clone(), this.players[clientID], this.newGameObject());
        }
        this.broadcast(new SpawnMessage(msg.tag, o.id, clientID, pos.x, pos.y));
        this.gameObjects.push(o);
        console.log(this.gameObjects);
        o.update(1);
        console.log("tried to update the factory");
        return true;
    }

    update(deltaTime){
        this.gameObjects.forEach((object) =>{object.update(deltaTime); });
    }

    addPlayer(socket: Socket) {        
        var player = new Player(this, socket, this.players.length);
        this.players.push(player);
    }
    newGameObject() : number {
        return this.gameObjectCounter++;
    }
}

GameObjects.ts

export import util = module("Utilities");
export import s = module("server");
export import g = module("game");

export interface GameObject{
    tag: g.UID;
    id:number;
    player: g.Player;
    clientId: number;
    pos:util.Vector2;
    getPos():util.Vector2;
    setPos(newPos:util.Vector2);
    // !TODO how to make that const?
    boundingBox: util.Rectangle;
    update(deltaTime:number);
}

export class Factory implements GameObject {
    tag: g.UID;
    id: number;
    player: g.Player;
    clientId: number;
    server: s.Server;
    //variables for handling the delay between spawning robots
    private current_time: number;
    public delay: number;
    boundingBox: util.Rectangle;

    public static dimensions = new util.Vector2(30, 30);
    constructor(pos: util.Vector2, player:g.Player, id: number) {
        this.pos = pos;
        this.tag = g.UID.FACTORY;
        this.player = player;
        this.clientId = this.player.getID();
        this.current_time = 0;
        this.delay = 1;
        this.id = id;
        this.boundingBox = new util.Rectangle(pos, Factory.dimensions.x, Factory.dimensions.y);
        console.log("just created a factory");
        //this.update(1);
    }

    pos: util.Vector2;
    getPos() { return this.pos; }
    setPos(pos: util.Vector2) { this.pos = pos; }

    public once = true;
    //check if it's time to create a new robot
    public update(deltaTime: number) {
        console.log("updating a factory");

        //this code will produce a robot just once, this is handy for development, since there's not so much waiting time
        if (this.once) { this.player.requestSpawn(g.UID.ROBOT, this.pos.x, this.pos.y); console.log("just spawned a robot"); }
        this.once = false;
        /*this.current_time += deltaTime/1000;
        if (this.current_time > this.delay*(Factory.count+1)/(Mine.count+1)) {
            this.current_time = 0;
            this.spawnRobot();
        }*/
    }
}


//this will be the fighting robot meant to destroy enemy factories
export class Robot implements GameObject{
    tag: g.UID;
    id: number;
    player:g.Player;
    clientId: number;
    game: g.Game;
    boundingBox: util.Rectangle;
    // ! TODO constants should have capital letters.
    public static radius = 15;
    constructor(pos:util.Vector2,player:g.Player,id:number){ 
        this.tag = g.UID.ROBOT;
        this.player=player;
        this.clientId = this.player.getID();
        this.boundingBox = new util.Rectangle(pos, Robot.radius, Robot.radius);
    }

    pos:util.Vector2;
    getPos(){return this.pos;}
    setPos(pos:util.Vector2){this.pos=pos;}

    //now the robot is moved by keyboard input but soon it will check the gameObjects array and search for the closest enemy,
    //in order to attack it
    public update(deltaTime: number) {

    }
}

Right now the first call of the update method of a factory instance spawns a robot, after that the factory "sleeps". Robots do nothing in their update method.

I would like to lead your eyes on two lines of the code: - In the requestSpawn method the GameObject is updated immediately with deltaTime=1

That means that directly after spawning a factory, a robot should be spawned, too. But that doesn't happen. I added a console.log call inside the requestSpawn method. It successfully prints "just tried to update a factory" but nevertheless, nothing happens. So I supposed that the update method was not working properly and added a console.log call there, too. It's the first line of the Factory's update method and should print "updating a factory". But that never happens.

I was really confused. The method should be called but it isn't. Although it is declared public, I thought that the problem might have something to do with access rights. So this here's the second line of code, that I would like to point out: - in the constructor of the Factory, I've commented out a call to this.update(1).

I figured that at least the very own constructor should be able to call the update method. Indeed, it is. When this line is not commented out, update is called once. The factory then tries to spawn a new robot and calls requestSpawn() on its instance of Game. As a consequence a new robot is created and a SpawnMessage goes out to all clients. The robot even appears in the client's browser tab.

Therefore it's obvious that the method is not called. Everything works fine, the message parsing, factory updating and robot creating are correct. The only problem is that all calls to update from within Game are not executed. What went wrong ?

Upvotes: 0

Views: 4278

Answers (1)

Jude Fisher
Jude Fisher

Reputation: 11284

You've posted a lot of code, and there may be other issues, but at least one is that you are missing the break in your switch statement...

switch (msg.tag) {
        case UID.FACTORY:                                        
            o = new go.Factory(pos.clone(), this.players[clientID], this.newGameObject());
        case UID.ROBOT:
            o = new go.Robot(pos.clone(), this.players[clientID], this.newGameObject());
}

...and therefore always creating a Robot, which traces nothing in its update() method. (If you try to create a Factory, you succeed, but then immediately overwrite it by assigning a new Robot to the same var o).

Consider this simplified example:

interface GameObject {
    update():void;
}

class Factory implements GameObject {
    update():void {
        console.log("Factory");
    }
}

class Robot implements GameObject {
    update():void {
        console.log("Robot");
    }
}

class Test {

    private o:GameObject;

    constructor(index:number){
        switch(index){
            case 1:
                this.o = new Factory();
            case 2:
                this.o = new Robot();
        }
        this.o.update();
    }
}

class BreakTest {

    private o:GameObject;

    constructor(index:number){
        switch(index){
            case 1:
                this.o = new Factory();
                break;
            case 2:
                this.o = new Robot();
                break; // Not necessary in final case, but good form IMO.
        }
        this.o.update();
    }
}

var t = new Test(1);       // Traces 'Robot'
var tt = new Test(2);      // Traces 'Robot'

var b = new BreakTest(1);  // Traces 'Factory'
var bt = new BreakTest(2); // Traces 'Robot'

(Ref)

Upvotes: 2

Related Questions