GoError: Failed to find InitModule function when using registerMatch

I just started working on the server side things of Nakama and wanted to make use of the matchLoop for my game in Unity. Looking at the documentation I saw that I had to use initializer.registerMatch to do that. I defined the needed functions in a separate file called “match_handler.ts”. After starting the server it gives the following errors:

server-nakama-1  | {"level":"error","ts":"2022-10-04T15:22:43.334Z","caller":"server/runtime_javascript.go:1601","msg":"Failed to eval JavaScript modules.","error":"GoError: failed to find InitModule function\n\tat github.com/heroiclabs/nakama/v3/server.(*RuntimeJavascriptInitModule).registerMatch.func1 (native)\n\tat InitModule (index.js:11:9(23))\n\tat native\n"}
server-nakama-1  | {"level":"error","ts":"2022-10-04T15:22:43.334Z","caller":"server/runtime.go:614","msg":"Error initialising JavaScript runtime provider","error":"GoError: failed to find InitModule function\n\tat github.com/heroiclabs/nakama/v3/server.(*RuntimeJavascriptInitModule).registerMatch.func1 (native)\n\tat InitModule (index.js:11:9(23))\n\tat native\n"}
server-nakama-1  | {"level":"fatal","ts":"2022-10-04T15:22:43.334Z","caller":"main.go:158","msg":"Failed initializing runtime modules","error":"GoError: failed to find InitModule function\n\tat github.com/heroiclabs/nakama/v3/server.(*RuntimeJavascriptInitModule).registerMatch.func1 (native)\n\tat InitModule (index.js:11:9(23))\n\tat native\n"}

It’s weird that it says that it failed to find the initModule, as I have defined that in my main.ts file, and it works when I remove the registerMatch call. I’ve tried many different examples and looking at existing repo’s but just can’t seem to fix it.

  1. Versions: Nakama 3.13.1, Windows/Docker
  2. Server Framework Runtime language (If relevant): TS/JS

match_handler.ts:

{// Match initialization function (runs once when the match is created either via rpc or through the matchmaker)
const matchInit: nkruntime.MatchInitFunction<nkruntime.MatchState> = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, params: {[key: string]: string}) : {state: nkruntime.MatchState, tickRate: number, label: string} {
    logger.debug('Match initialized.');
    return {
            state: { },
            tickRate: 1,
            label: ''
    };
};

// When a player tries to join
const matchJoinAttempt: nkruntime.MatchJoinAttemptFunction<nkruntime.MatchState> = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, dispatcher: nkruntime.MatchDispatcher, tick: number, state: nkruntime.MatchState, presences: nkruntime.Presence, metadata : {[key : string]: any}) : {state: nkruntime.MatchState, accept: boolean } | null {        
    return {
        state,
        accept: true
    };
};

// When one (or multiple) player(s) actually join(s)
const matchJoin: nkruntime.MatchJoinFunction<nkruntime.MatchState> = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, dispatcher: nkruntime.MatchDispatcher, tick: number, state: nkruntime.MatchState, presences: nkruntime.Presence[]) : {state: nkruntime.MatchState} | null {
    return {state};
};

// When a player leaves
const matchLeave: nkruntime.MatchLeaveFunction<nkruntime.MatchState> = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, dispatcher: nkruntime.MatchDispatcher, tick: number, state: nkruntime.MatchState, presences: nkruntime.Presence[]) : {state: nkruntime.MatchState} | null {
    return {state};
};

// Runs every tick
const matchLoop: nkruntime.MatchLoopFunction<nkruntime.MatchState> = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, dispatcher: nkruntime.MatchDispatcher, tick: number, state: nkruntime.MatchState, messages: nkruntime.MatchMessage[]) : {state: nkruntime.MatchState} | null {
    return {state};
};

// Runs when the match gets terminated
const matchTerminate: nkruntime.MatchTerminateFunction<nkruntime.MatchState> = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, dispatcher: nkruntime.MatchDispatcher, tick: number, state: nkruntime.MatchState, graceSeconds: number) : {state: nkruntime.MatchState} | null {
    return {state};
};

const matchSignal: nkruntime.MatchSignalFunction<nkruntime.MatchState> = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, dispatcher: nkruntime.MatchDispatcher, tick: number, state: nkruntime.MatchState, data: string) : {state: nkruntime.MatchState, data?: string} | null {
    logger.debug('Lobby match signal recieved: ' + data);
    return {state, data};
};}

Main.ts:

const InitModule: nkruntime.InitModule = function(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, initializer: nkruntime.Initializer) {
    logger.debug("Hello World!!@!!");

    initializer.registerMatch('match', {
        matchInit,
        matchJoinAttempt,
        matchJoin,
        matchLeave,
        matchLoop,
        matchSignal,
        matchTerminate
      });
}

Hey @AngelMashiro are you adding your new file to the files section of your tsconfig.json as shown here in this example: Nakama: TypeScript Runtime | Heroic Labs Documentation

Hey, I have added both my files to my tsconfig.json, it looks as followed:

{
  "files": [
    "./main.ts",
    "./match_handler.ts",
  ],
  "compilerOptions": {
    "target": "es2015",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "typeRoots": [
      "./node_modules"
    ], 
   "outFile": "./build/index.js",
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
   "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
    "strict": true,                                      /* Enable all strict type-checking options. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  }
}

The filenames in the config match those that exist in the folder

Okay, if you run npx tsc you should see that your index.js file has updated. Do you still hit the same error?

I use Docker to run the server, and the Dockerfile compiles the typescript files. But if I manually run the command I get a bunch of errors (71 to be exact) that it can’t find a definition file, example of last 4 being:

error TS2688: Cannot find type definition file for 'type-check'.
  The file is in the program because:
    Entry point for implicit type library 'type-check'

error TS2688: Cannot find type definition file for 'which'.
  The file is in the program because:
    Entry point for implicit type library 'which'

error TS2688: Cannot find type definition file for 'wrappy'.
  The file is in the program because:
    Entry point for implicit type library 'wrappy'

error TS2688: Cannot find type definition file for 'yallist'.
  The file is in the program because:
    Entry point for implicit type library 'yallist'

None of which I even use, but I assume that’s what Nakama uses.

In case it’s relevant, this is the dockerfile that I normally use to start the server:

FROM node:alpine AS node-builder

WORKDIR /backend

COPY package*.json .
RUN npm install

COPY tsconfig.json .
COPY *.ts .
RUN npx tsc

FROM registry.heroiclabs.com/heroiclabs/nakama:3.13.1

COPY --from=node-builder /backend/build/*.js /nakama/data/modules/build/
COPY local.yml .

I tried manually compiling the files as you asked me to. I hadn’t installed nakama-common yet.
On the Typescript runtime page it says to install it with npm i 'https://github.com/heroiclabs/nakama-common', but that didn’'t work for me. It gave me an error that that repository doesn’t have a package.json file:

npm ERR! Could not install from "'https:\github.com\heroiclabs\nakama-common'" as it does not contain a package.json file.

So I installed it using npm install heroiclabs/nakama-common, which I believe achieves the same thing.

I don’t get errors when manually compiling it now, and I can see the index.js file in my build folder update. I still get the same error as before however.

Hey @AngelMashiro what version of node and npm are you on?

Hey, I was running an older version of node (v10.x). I just realised I missed the part where it said the prerequisite was Node v14 or later. I updated my node (with neccesary tools) and npm and that seemed to have fixed it. Thank you for pointing it out!