Roblox/Minecraft server link

From ihaveahax's Site
< Roblox
Revision as of 18:01, 23 December 2020 by Ihaveahax (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

I once created a very simple link between a Roblox and Minecraft Java game server. It only reported positions from the Minecraft world to Roblox. Later I made something that could return chunks from the Minecraft world and recreate it in Roblox but the code for that is buried somewhere. (Maybe it would be more efficient these days due to parallel Luau!)

The Minecraft side uses the CommandHelper plugin, which uses the MethodScript language.

This does NOT work on a live Roblox server since it expects there to be little to no latency (it doesn't deal with this well). And Roblox may not like the high volume of HTTP requests going out. It also does not filter Minecraft usernames, which would not work that well anyway.

CommandHelper main.ms

<!
	strict;
	author: Ian Burgwin (ihaveahax)
	created: 2020-03-17
	description: Return player positions. (Eventually will handle Roblox player positions.)
>
httpd_listen(29295) #-- 0x726F (ro)

bind('http_request', null, null, @event) {
	modify_event('code', 200);
	httpd_set_header('Content-Type', 'application/json');

	array @positions = array();

	array @smaller_ploc;
	array @c; # current position
	foreach(all_players(), @player) {
		@c = location_shift(ploc(@player), 'UP', 1);
		@smaller_ploc = array('x': @c['x'], 'y': @c['y'], 'z': @c['z'], 'pitch': @c['pitch'], 'yaw': @c['yaw']);
		@positions[@player] = @smaller_ploc;
	}

	modify_event('body', json_encode(@positions));
}

Roblox object hierarchy

  • Workspace
    • MCCurrentPlayers (Folder)
  • ServerScriptService
    • MCServerLink (Folder)
      • HttpHandler (Script)
      • PlayerHandler (Script)
  • ServerStorage
    • MCServerLink (Folder)
      • MakeRequest (BindableEvent)
      • PlayerAdded (BindableEvent)
      • PlayerMove (BindableEvent)
      • PlayerRemoving (BindableEvent)
      • Base (Model)

ServerScriptService.MCServerLink.HttpHandler

Makes a request to the server roughly every 1/6th of a second, making up to 360 requests per minute.

local HttpService = game:GetService('HttpService')
local ServerStorage = game:GetService('ServerStorage')

local Storage = ServerStorage.MCServerLink
local MakeRequest = Storage.MakeRequest
local PlayerAdded = Storage.PlayerAdded
local PlayerRemoving = Storage.PlayerRemoving
local PlayerMove = Storage.PlayerMove

local Url = 'http://localhost:29295'

-- key is the username, value is always true
-- this loses order, but is faster than an ordered table
local CurrentPlayers = {}

local function HandleMakeRequest()
	print('Requesting...')
	local Positions = HttpService:JSONDecode(HttpService:GetAsync(Url))
	print('Got data')
	for Username, _ in pairs(CurrentPlayers) do
		if not Positions[Username] then
			print('Player removed: '..Username)
			PlayerRemoving:Fire(Username)
		end
	end
	
	for Username, PositionData in pairs(Positions) do
		local Position = Vector3.new(PositionData['x'], PositionData['y'], PositionData['z'])
		local Orientation = Vector3.new(0, PositionData['yaw'], -PositionData['pitch'])
		if not CurrentPlayers[Username] then
			print('Player added: '..Username)
			PlayerAdded:Fire(Username, Position, Orientation)
			CurrentPlayers[Username] = true
		else
			print('Player moved: '..Username)
			PlayerMove:Fire(Username, Position, Orientation)
		end
	end
end

MakeRequest.Event:Connect(HandleMakeRequest)

print('HttpHandler ready')

while wait(1/6) do
	MakeRequest:Fire()
end

ServerScriptService.MCServerLink.PlayerHandler

local HttpService = game:GetService('HttpService')
local ServerStorage = game:GetService('ServerStorage')
local TextService = game:GetService('TextService')
local TweenService = game:GetService('TweenService')

local Storage = ServerStorage.MCServerLink
local PlayerAdded = Storage.PlayerAdded
local PlayerRemoving = Storage.PlayerRemoving
local PlayerMove = Storage.PlayerMove
local BaseModel = Storage.Base

local CurrentPlayersFolder = workspace.MCCurrentPlayers

local TInfo = TweenInfo.new(1/6, Enum.EasingStyle.Linear, Enum.EasingDirection.In, 0, false)

local function GetPlayerModel(Username)
	local Model = CurrentPlayersFolder:FindFirstChild(Username)
	if not Model then
		Model = BaseModel:Clone()
		Model.Name = Username
		Model.Head.NameGui.NameLabel.Text = Username
		-- not depending on the label here
		local TextSize = TextService:GetTextSize(Username, 100, Enum.Font.Gotham, Vector2.new(2000, 100))
		Model.Head.NameGui.Background.Size = UDim2.new((TextSize.X / 2000) + 0.01, 0, 1, 0)
		Model.Parent = CurrentPlayersFolder
	end
	return Model
end

local function MovePlayer(Model, Position, Orientation)
	local CF = CFrame.Angles(0, -math.rad(Orientation.Y + 180), 0)
	CF = CF:ToWorldSpace(CFrame.new())
	local Goal = {['CFrame'] = CF + (Position * 4)}
	local Tween = TweenService:Create(Model.PrimaryPart, TInfo, Goal)
	Tween:Play()
end

local function HandlePlayerChange(Username, Position, Orientation)
	local Model = GetPlayerModel(Username)
	MovePlayer(Model, Position, Orientation)
end

local function HandlePlayerRemove(Username)
	local Model = CurrentPlayersFolder:FindFirstChild(Username)
	if Model then
		Model:Destroy()
	end
end

PlayerAdded.Event:Connect(HandlePlayerChange)
PlayerMove.Event:Connect(HandlePlayerChange)
PlayerRemoving.Event:Connect(HandlePlayerRemove)