MineDojo / Voyager

An Open-Ended Embodied Agent with Large Language Models
https://voyager.minedojo.org/
MIT License
5.53k stars 512 forks source link

First person view is possible, at least with mc_port #44

Closed TimeLordRaps closed 1 year ago

TimeLordRaps commented 1 year ago

In single player open to lan with cheats and survival, use the port for mc_port, once the bot joins. Then run /gamemode spectator command in in-game text, from the player in single player, then run /spectate bot command in in-game text.

This should work until the bot leaves and rejoins. Then you just need to rerun the /spectate bot command. There is definitely a way to automate this with repeating command blocks and chains, but idk enough about command blocks to be able to figure it out right now.

xieleo5 commented 1 year ago

Hi, thanks for pointing out this method! You're right that commands in games can help the player follow the first-person view of the bot. However, we still cannot see the bot's status bar in the game. Also, this needs some human intervention during the learning process. One possible solution could be using a command block to consistently execute the /spectate command.

go-maple commented 1 year ago

@TimeLordRaps @xieleo5 Tks all.When I try to run the code,I can't see the first view.I feel a little nervous.I told myself where's my bot, What's doing the bot in this moment.:)

TimeLordRaps commented 1 year ago

@go-maple what code, the in-game commands?

The first in-game command: /gamemode spectator puts your gamemode into spectator, which is just a way to spectate the world, without interacting with it. While in spectator gamemode you can go through walls and other things with standard movement controls. The secondin-game command: /spectate bot should immediately transport you into the body of the bot, as long as the bot is on the server and you are in the spectator gamemode.

To see your bot move, you can observe from the default first person perspective or to see from a 3rd person perspective: While inside the bot, or generally, press f5, then pressing f5 again will give a perspective looking straight at the character's face, then f5 for a third time will bring it back to first person perspective. All these things I am talking about occur from within an mc_port of the game. So a single player mc launcher version of the game. I have not experimented at all using the azure login.

TimeLordRaps commented 1 year ago

To clarify, when the bot leaves the server, you no longer are spectating it, however you remain in spectator gamemode. Once the bot rejoins the server, you need to reuse the second in-game command: /spectate bot to once again spectate from the bot's perspective(s).

theblazehen commented 1 year ago

I wrote a script to automate it a bit. It requires the following settings in the minecraft server:

root@mc-voyager:~/mc_server# grep rcon server.properties
broadcast-rcon-to-ops=false
enable-rcon=true
rcon.password=hunter2
rcon.port=25575

Then the following script, updating the variables where relevant. Make sure to pip install mcrcon before running.

import time
from mcrcon import MCRcon
import math
import re

# Server RCON connection details
rcon_host = "localhost"
rcon_port = 25575
rcon_password = "hunter2"

# Time interval (in seconds) to check for player joining and teleport
check_interval = 0.1

# Dictionary to track the online status of the specified player and bot
spectator_username = "theblazehen"
player_status = {spectator_username: False, "bot": False}
previous_player_status = player_status.copy()

# Distance to move away from bot in the X and Z directions
distance = 3

# Height offset for the specified player above bot
height_offset = 1

def main():
    global player_status, previous_player_status
    while True:
        with MCRcon(rcon_host, rcon_password, rcon_port) as mcr:
            response = mcr.command("list")
            players_string = response.strip().split(": ")[1]

            # Handle the case when there are no players online
            if players_string == "":
                players_online = []
            else:
                players_online = players_string.split(", ")

            # Update the online status of the specified player and bot
            for player in player_status:
                player_status[player] = player in players_online

            # Check if the online status of either the specified player or bot has changed
            if player_status != previous_player_status:
                previous_player_status = player_status.copy()

                # Check if both the specified player and bot are online
                if player_status[spectator_username] and player_status["bot"]:
                    mcr.command(f"gamemode spectator {spectator_username}")

            # Continuously teleport the specified player behind bot and face towards bot's back
            if player_status[spectator_username] and player_status["bot"]:
                response = mcr.command("data get entity bot Pos")
                bot_position = [float(coord) for coord in re.findall(r"[-+]?\d*\.\d+|\d+", response)]

                response = mcr.command("data get entity bot Rotation")
                bot_rotation = [float(coord) for coord in re.findall(r"[-+]?\d*\.\d+|\d+", response)]

                x, y, z = bot_position
                yaw = bot_rotation[0]

                # Calculate new position for the specified player
                x_new = x - distance * math.sin(math.radians(yaw))
                z_new = z - distance * math.cos(math.radians(yaw))

                # Calculate pitch and yaw to face bot's back
                dx, dy, dz = x - x_new, y - (y + height_offset), z - z_new
                distance_2d = math.sqrt(dx * dx + dz * dz)
                pitch = -math.degrees(math.atan2(dy, distance_2d))
                dyaw = math.degrees(math.atan2(dx, dz))
                yaw_to_face_bot = (dyaw + 180) % 360

                # Teleport the specified player to the new position and face towards bot's back
                mcr.command(f"tp {spectator_username} {x_new} {y + height_offset} {z_new} {yaw_to_face_bot} {pitch}")

        time.sleep(check_interval)
if __name__ == "__main__":
    main()
TimeLordRaps commented 1 year ago

I wrote a script to automate it a bit. It requires the following settings in the minecraft server:

I haven't tested it yet, but why face the bot's back. Wouldn't you want the exact direction it's facing, and location to emulate "being" the bot, or is your intention to have a solid view of the bot, if so assuming this would run as does what it looks like it would do a pretty good job of it.

I do not know about the library you used, but I assume there's a way maybe to set the inventory and status of the player to the bot? Well, I guess that wouldn't really work for complete recording purposes because spectator mode doesn't show those things. So best case scenario I can imagine is 2 players, 1 used to record hotbar, health, and hunger, and the other recording the first-person view, then somehow having to combine the two recordings. Otherwise, I think you have more or less solved the general recording problem quite well. Unless someone wants to try to implement a solution for first-person with status, I think I'm going to close this.

GoingMyWay commented 1 year ago

In single player open to lan with cheats and survival, use the port for mc_port, once the bot joins. Then run /gamemode spectator command in in-game text, from the player in single player, then run /spectate bot command in in-game text.

This should work until the bot leaves and rejoins. Then you just need to rerun the /spectate bot command. There is definitely a way to automate this with repeating command blocks and chains, but idk enough about command blocks to be able to figure it out right now.

I can see the first person view and hear the sound of the game.

GoingMyWay commented 1 year ago

I wrote a script to automate it a bit. It requires the following settings in the minecraft server:

root@mc-voyager:~/mc_server# grep rcon server.properties
broadcast-rcon-to-ops=false
enable-rcon=true
rcon.password=hunter2
rcon.port=25575

Then the following script, updating the variables where relevant. Make sure to pip install mcrcon before running.

import time
from mcrcon import MCRcon
import math
import re

# Server RCON connection details
rcon_host = "localhost"
rcon_port = 25575
rcon_password = "hunter2"

# Time interval (in seconds) to check for player joining and teleport
check_interval = 0.1

# Dictionary to track the online status of the specified player and bot
spectator_username = "theblazehen"
player_status = {spectator_username: False, "bot": False}
previous_player_status = player_status.copy()

# Distance to move away from bot in the X and Z directions
distance = 3

# Height offset for the specified player above bot
height_offset = 1

def main():
    global player_status, previous_player_status
    while True:
        with MCRcon(rcon_host, rcon_password, rcon_port) as mcr:
            response = mcr.command("list")
            players_string = response.strip().split(": ")[1]

            # Handle the case when there are no players online
            if players_string == "":
                players_online = []
            else:
                players_online = players_string.split(", ")

            # Update the online status of the specified player and bot
            for player in player_status:
                player_status[player] = player in players_online

            # Check if the online status of either the specified player or bot has changed
            if player_status != previous_player_status:
                previous_player_status = player_status.copy()

                # Check if both the specified player and bot are online
                if player_status[spectator_username] and player_status["bot"]:
                    mcr.command(f"gamemode spectator {spectator_username}")

            # Continuously teleport the specified player behind bot and face towards bot's back
            if player_status[spectator_username] and player_status["bot"]:
                response = mcr.command("data get entity bot Pos")
                bot_position = [float(coord) for coord in re.findall(r"[-+]?\d*\.\d+|\d+", response)]

                response = mcr.command("data get entity bot Rotation")
                bot_rotation = [float(coord) for coord in re.findall(r"[-+]?\d*\.\d+|\d+", response)]

                x, y, z = bot_position
                yaw = bot_rotation[0]

                # Calculate new position for the specified player
                x_new = x - distance * math.sin(math.radians(yaw))
                z_new = z - distance * math.cos(math.radians(yaw))

                # Calculate pitch and yaw to face bot's back
                dx, dy, dz = x - x_new, y - (y + height_offset), z - z_new
                distance_2d = math.sqrt(dx * dx + dz * dz)
                pitch = -math.degrees(math.atan2(dy, distance_2d))
                dyaw = math.degrees(math.atan2(dx, dz))
                yaw_to_face_bot = (dyaw + 180) % 360

                # Teleport the specified player to the new position and face towards bot's back
                mcr.command(f"tp {spectator_username} {x_new} {y + height_offset} {z_new} {yaw_to_face_bot} {pitch}")

        time.sleep(check_interval)
if __name__ == "__main__":
    main()

Cool. Did you manage to run the Voyager code with the MC server?

GoingMyWay commented 1 year ago

@theblazehen I tried to run your code by the following steps:

  1. Install mcrcon via pip
  2. Create a folder call mc_server in $HOME.
  3. Add a file called server.properties and put the following content into it:
    broadcast-rcon-to-ops=false
    enable-rcon=true
    rcon.password=hunter2
    rcon.port=25575
  4. Run the Python script in the same directory.

But in step 4, I got the following issue:

raceback (most recent call last):
  File "/Users/me/mc_server/mc_auto_spectate_bot.py", line 79, in <module>
    main()
  File "/Users/me/mc_server/mc_auto_spectate_bot.py", line 28, in main
    with MCRcon(rcon_host, rcon_password, rcon_port) as mcr:
  File "/Users/me/miniconda3/lib/python3.9/site-packages/mcrcon.py", line 57, in __enter__
    self.connect()
  File "/Users/me/miniconda3/lib/python3.9/site-packages/mcrcon.py", line 77, in connect
    self.socket.connect((self.host, self.port))
ConnectionRefusedError: [Errno 61] Connection refused

Do you know why?

theblazehen commented 1 year ago

@GoingMyWay It requires you to be running a standalone minecraft server. As mentioned in #88 , will be writing the docs for that later today

GoingMyWay commented 1 year ago

@GoingMyWay It requires you to be running a standalone minecraft server. As mentioned in #88 , will be writing the docs for that later today

@theblazehen Cool. Thank you. I am looking forward to it.

ShaoTengLiu commented 1 year ago

@theblazehen Hi, thanks for your useful scripts.

After running your mcron script, I continuously get the following info: [15:55:08] [RCON Client /127.0.0.1 #999/INFO]: Thread RCON Client /127.0.0.1 shutting down [15:55:08] [RCON Listener #1/INFO]: Thread RCON Client /127.0.0.1 started

However, the view in my minecraft doesn't move. Do you have any suggestions on it?

Ps: I have a gui, so I just link to the local server in "multiple players".

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] commented 1 year ago

This issue was closed because it has been inactive for 14 days since being marked as stale.