RPG Maker MV with Solana SDK connect to Phantom wallet

RPG Maker MV with Solana SDK connects to Phantom wallet

    Hi guys, recently i've seen RPG Maker showcase they bring 2D-HD features, i'm excited. So i'm gonna do Solana Integration series, then i decided to picked up "RPG Maker MV" to integrate Solana SDK and yes, it's work like a charm. Anyone using RPG Maker and never integration Solana, let's try.

Based on connect phantom wallet, disconnect wallet then get SOL balance via Solana Web3 SDK and pay with Solana. Check out below.

Required *download, setup, and installation

Windows 11

RPG Maker MV [download]

Phantom Wallet desktop version [download**beware fake Phantom App

Http-server [download] *simple local server, easy and enough for testing.

Visual Studio Code [download] * Browse to open the project folder and code.


Optional (Required for testing only)

Microsoft Edge [download] *or other browsers that support the Phantom Wallet browser extension.


Setup Phantom App [for testing only]

    Download Phantom Wallet app and install it from the previous section. After that, open and login in with your password. **if no account, you can follow this instruction to create a new wallet. 

    After login, you need to click on Avatar, then choose account and then click on Settings > Developer Settings, turn on Testnet mode and select Solana Devnet mode.

    If your settings were corrected, you should see a notice on your Phantom app like this.  "You are currently in Testnet Mode. 


Get free SOL for testing

    If your account not having SOL, you cannot pay for anything because any transaction required gas fee. Open this url to request an Airdrop , then copy your public address and paste here, select SOL amount you need then click Confirm Airdrop.


    If it's successful, you'll see a message on the bottom-right of screen.

    

    Then take a look SOL in your Phantom Wallet, here your SOL ready for testing. **I recommended to create 2 wallet accounts for testing transfer one to another account.

Create a new project.

Launch RPG Maker MV, click "continue" if you don't have the product's key (20-day free trial). waiting for a while.


Once an application opened, choose File > New Project..., create project "Name" and browse to "Location" you need to save the project and wait for the project to be initialized.

Click the green play button on the top-right; your project should launch like below. Close game.


Export as Web

Go to File > Deployment....


Choose "Web browsers", and choose the exported location, then click "OK".


Waiting for a while, your game should be exported


Test run Web game

We have to use "http-server" for testing the web on localhost, If you're not familiar with that, please install and set it up from the "Required" section first. Open cmd from Windows search, type "cd C:\your_path\RPGmakerMV-connect-wallet" then hit "Enter".


Here you go, just hold "Ctrl" and click on "http://127/0/0/1:8080" or copy and paste "http://127/0/0/1:8080" on your browser.


Just want to let you know how to export a web game and test it on your machine without an actual server. Remember this method; we need to export it when this course finishes.

Create SolanaBridge plugin 
    under "js/plugins" create a new file "SolanaBridge.js". we start with blank global function.

(function() {

})();

at the top inside function we need to import solana web3 sdk, make sure no other plugin already imported.

  const script = document.createElement("script");
  script.src = "https://unpkg.com/@solana/web3.js@latest/lib/index.iife.js";
  document.body.appendChild(script);

then at title scene we need to handle connect and disconnect phantom app. Also keep wallet address to use in get SOL balance later, once "connect wallet" like login game before start "new game", then we can "disconnect" from wallet like logout game

// TITLE SCENE
  window.WALLET_CONNECTED = false;
  window.WALLET_ADDRESS = null;

  const _createCommandWindow = Scene_Title.prototype.createCommandWindow;

  Scene_Title.prototype.createCommandWindow = function() {
    _createCommandWindow.call(this);

    this._commandWindow.setHandler("connectWallet",
      this.commandConnectWallet.bind(this)
    );

    this._commandWindow.setHandler("disconnectWallet",
      this.commandDisconnectWallet.bind(this)
    );
  };

  Scene_Title.prototype.commandConnectWallet = async function() {

    if (!window.solana || !window.solana.isPhantom) {
      alert("Please install Phantom Wallet");
      return;
    }

    try {
      const resp = await window.solana.connect();

      window.WALLET_CONNECTED = true;
      window.WALLET_ADDRESS = resp.publicKey.toString();

      $gameVariables.setValue(1, window.WALLET_ADDRESS);

      await window.fetchSolBalance();
    } catch (e) {
      console.warn("Connection failed", e);
    }

    this._commandWindow.activate();
    this._commandWindow.refresh();
  };

On title scene, default menu is "New Game", "Continue" and "Options", now we need to add "Connec t Wallet" at the top first menu and not allow player "New Game" or "Continue" , once connected wallet, top menu will be change to "Disconnect" to logout and back to "Connect Wallet" menu again.

// TITLE COMMAND
  const _makeCommandList = Window_TitleCommand.prototype.makeCommandList;

  Window_TitleCommand.prototype.makeCommandList = function() {
    _makeCommandList.call(this);

    this.addCommand("Connect Wallet", "connectWallet");
  };
  Window_TitleCommand.prototype.isNewGameEnabled = function() {
    return window.WALLET_CONNECTED;
  };

  Window_TitleCommand.prototype.makeCommandList = function() {
    if (window.WALLET_CONNECTED) {
      this.addCommand("Disconnect", "disconnectWallet");
    } else {
      this.addCommand("Connect Wallet", "connectWallet");
    }
    this.addCommand("New Game", "newGame", this.isNewGameEnabled());
    this.addCommand("Continue", "continue", this.isContinueEnabled());
    this.addCommand("Options", "options");
  };

After "New Game", title will be change to map scene, right-click on map fields to open menu, at left-bottom of window you should see "0 G" in-game currency.

So we need "0 SOL" currency display on above of "0 G" too. then back to "SolanaBridge.js" continue after last function.

// IN-GAME MENU, SOL BALANCE
  const _Scene_Menu_create = Scene_Menu.prototype.create;

  Scene_Menu.prototype.create = function() {
    _Scene_Menu_create.call(this);

    this.createSolWindow();
  };

  Scene_Menu.prototype.createSolWindow = function() {

    const goldWindow = this._goldWindow;

    const x = goldWindow.x;
    const y = goldWindow.y - goldWindow.height;

    this._solWindow = new Window_Sol(x, y);
    this.addWindow(this._solWindow);
  };

  function Window_Sol() {
    this.initialize.apply(this, arguments);
  }

  Window_Sol.prototype = Object.create(Window_Base.prototype);
  Window_Sol.prototype.constructor = Window_Sol;

  Window_Sol.prototype.initialize = function(x, y) {
    const width = 240;
    const height = this.fittingHeight(1);
    Window_Base.prototype.initialize.call(this, x, y, width, height);
    this.refresh();
  };

  Window_Sol.prototype.refresh = function() {
    this.contents.clear();

    const value = window.SOL_BALANCE || 0;

    const x = 0;
    const width = this.contents.width;

    // draw number (normal color)
    this.resetTextColor();
    this.drawText(value.toFixed(3), x, 0, width - 54, "right");

    // draw "SOL" with system color (same as G)
    this.changeTextColor(this.systemColor());
    this.drawText("SOL", width - 66, 0, 60, "right");

    // reset after use
    this.resetTextColor();
  };

  window.fetchSolBalance = async function() {
    try {
      const connection = new solanaWeb3.Connection(
        solanaWeb3.clusterApiUrl("devnet"),
        "confirmed"
      );

      const pubkey = new solanaWeb3.PublicKey(window.WALLET_ADDRESS);

      const lamports = await connection.getBalance(pubkey);

      window.SOL_BALANCE = lamports / solanaWeb3.LAMPORTS_PER_SOL;

      console.log("SOL Balance:", window.SOL_BALANCE);

    } catch (e) {
      console.error("Balance fetch error", e);
    }
  };

Above fetchSolBalance is required import solana web3 sdk at the top of file, and we set cluster to "devnet" for testing or development only. We created a new window and put above realted window "0 G" position, so on we need to draw text color style same as "0 G".

One last thing at this step add custom command for own plugin at the bottom part.

// PLUG-IN command
  const _Game_Interpreter_pluginCommand =
    Game_Interpreter.prototype.pluginCommand;

  Game_Interpreter.prototype.pluginCommand = function(command, args) {
    _Game_Interpreter_pluginCommand.call(this, command, args);

    if (command === "SolBridgeCommand") {
      console.log("Command triggered:", args);
    }
  };

Test SolanaBridge plugin
back to RPG Maker MV Map Editor, choose "Tools > Plugin Manager...", bdouble click on blank tab, choose "SolanaBridge" plugin and don't forget to turn "ON". before click "OK". then click "Apply" and "OK" to close Plugin Manager window.


Now we need to test 3 function, "connect wallet", "disconnect" and "get SOL balance", let exported as web like previous section and run with "http-server" to open localhost. Your browser should be like this.


Let's play, click "Connect Wallet" (login Phantom app if session expired) choose "connect". After connect success, your menu should be change to "Disconnect" and "New Game" is active state.

Click "New Game" then screen should be change to map, right-click to open "Menu" look at bottom-left you should see "xxx SOL" box above "0 G".
Cool! just leave out game by choose "Game End" > "To Title", you should see "Disconnect" and "New Game" active again.


Then click "Disconnect" your game should back to "Connect Wallet" menu and "New Game" is inactive.

Create Pay with SOL
Back to RPG Maker MV map editor, last function we need is buy item but on default map is nothing, just green grass field. Select on "MAP001" then double-click on grass grid block you need.

i picked up one people image for represent as Shopkeeper, and double click node under contentm then choose "Show Text..."

I add message "Welcomme to SOL Shop!" and pick an avatar image to match sprite character, click OK.


you can preview before click OK again.

double click node to add content again then choose "Show Choices...", add first choice "Buy Potion (0.002 SOL)" and second choice "Cancel", then click OK.


Now your content should be like this, then double click node under "When Buy Potion (0.002 SOL)"


Go to tab [3] and choose "Script..." type "payWithSol(0.002, 1)" then click OK.



Check on screen content again shopuld be like this, then click "apply".

Back to "SolanaBridge.js" implement "payWithSol" function after endof "fetchSolBalance", don't forget to add "receiver wallet address" saved.

window.payWithSol = async function(priceSol, itemId) {

    if (!window.solana || !window.WALLET_CONNECTED) {
      alert("Connect wallet first!");
      return;
    }

    try {
      const provider = window.solana;
      const connection = new solanaWeb3.Connection(
        solanaWeb3.clusterApiUrl("devnet"),
        "confirmed"
      );

      const fromPubkey = provider.publicKey;

      // YOUR RECEIVER WALLET (change this)
      const toPubkey = new solanaWeb3.PublicKey("RECEIVER_WALLET_ADDRESS");

      const transaction = new solanaWeb3.Transaction().add(
        solanaWeb3.SystemProgram.transfer({
          fromPubkey,
          toPubkey,
          lamports: priceSol * solanaWeb3.LAMPORTS_PER_SOL,
        })
      );

      transaction.feePayer = fromPubkey;

      const { blockhash } = await connection.getLatestBlockhash();
      transaction.recentBlockhash = blockhash;

      // Phantom sign + send
      const signed = await provider.signTransaction(transaction);
      const txid = await connection.sendRawTransaction(signed.serialize());

      await connection.confirmTransaction(txid);

      console.log("Transaction success:", txid);

      // Give item AFTER success
      $gameParty.gainItem($dataItems[itemId], 1);

      // refresh SOL UI
      if (window.fetchSolBalance) {
        await window.fetchSolBalance();
      }

    } catch (e) {
      console.error(e);
      alert("Transaction failed");
    }
  };


Don't forget to change RECEIVER_WALLET_ADDRESS to your receive SOL wallet.

// YOUR RECEIVER WALLET (change this)
      const toPubkey = new solanaWeb3.PublicKey("xxxx");

Test Pay with SOL
then "File > Deployment... > Web Browser > Overwrite", if you're running http-server without quit it, just refresh your tab game on browser and test buy potion, by move your heroes to beside Shopkeeper then click on "Shopkeeper", click "Buy Potion (0.002 SOL)"

It trigger Phantom app, (if session expired, you need to login again) then click "Confirm"

check on console log, you should see, transaction success and SOL balance updated.
 

open menu, you should see your SOL balance change.

click on menu "item", you should see "Potion" that bought from Shopkeeper recently.


Done!, now your game ready to sell any item with SOL coins.
Hope you enjoy, see you next time ;)

Buy Me a Coffee.

Free Download Content Updated!!

Provide RPG Maker MV - SolanaBridge Plugin
>> Download <<