Project1
标题: 通过DLL发包进行的RMXP服务器联机已获得初步进展 [打印本页]
作者: 零教授 时间: 2018-2-28 06:29
标题: 通过DLL发包进行的RMXP服务器联机已获得初步进展
RMXP的联机脚本可能这不是第一个,而且DLL扩展应该也是非常容易的
所以之前肯定有人研究过通过DLL进行的联机
本DLL采用的TCP联机,通过向服务端发送用户信息并进行匹配发包同步数据达成多终端联机
地图对象生成方面采用的是先建立默认事件再通过复制该事件到指定位置并改变一些参数
达到多个玩家同屏游玩
由于NPC移动速度和玩家移动速度并不一致的问题,数据同步会有0.2-0.8秒的延迟,由于不影响机能目前暂不解决
NPC映射方面相关代码如下
# ▼▲▼ TCP服务器映射 ▼▲▼
# by 零教授
#
#!需DLL和服务器支持!
#==============================================================================
# ■ Window_Base
#==============================================================================
$TCPput = Win32API.new('tcpforRMXP', 'tcpput', %w(i i i p), 'v')
$TCPget = Win32API.new('tcpforRMXP', 'tcpget', 'v','p')
$JOIN = Win32API.new('tcpforRMXP', 'join', 'v','v')
$Name = KOISHI::GetPrivateProfileString("Base","Name")
Map001 = load_datax(sprintf("Data/Map%03d.rxdata", 1))
$other_players = {}
$map_players = []
$old_x = 0
$old_y = 0
class Game_Map
alias setup_tcp setup
def setup(map_id)
setup_tcp(map_id)
#$map_players = []
# for i in 0...10
# @events[i+2000].erase if @events[i+2000] != nil
# end
#p $other_players
if $map_players.size > 0
for i in 0...$map_players.size
player = $other_players[$map_players[i]]
if player[2].to_i == map_id
#p $other_players[i][3]
@events[i+2000] = Game_Event.new(map_id, Map001.events[1])
@events[i+2000].erased = false
@events[i+2000].name = player[3]
@events[i+2000].character_hue = rand(360)
@events[i+2000].moveto(player[0].to_i,player[1].to_i)
@events[i+2000].refresh
#@events[i+2000].x = $other_players[i][0].to_i
#@events[i+2000].y = $other_players[i][1].to_i
#p @events[i+2000]
end
end
end
end
alias update_tcp update
def update
update_tcp
#if (Graphics.frame_count % 40) == 1
if $old_x != $game_player.x or $old_y != $game_player.y
$TCPput.call($game_player.x ,$game_player.y ,@map_id ,$Name)
$old_x, $old_y = $game_player.x, $game_player.y
end
begin
string = $TCPget.call
rescue
string = ""
end
# p string
tplayer = string.split(",")
if string != "" && tplayer.size == 5
if $other_players[tplayer[4]] == nil
$map_players.push(tplayer[4])
end
$other_players[tplayer[4]] = tplayer
if tplayer[2] == 0
$map_players.delete(tplayer[4])
$other_players[tplayer[4]] = nil
$game_temp.player_transferring = true
$game_temp.player_new_map_id = @map_id
$game_temp.player_new_x = $game_player.x
$game_temp.player_new_y = $game_player.y
$game_temp.player_new_direction = 5
$game_player_shock_flag = true
return
end
end
#p $other_players
#for i in 0..100-$other_players.size
# @events[2100-i] = nil
#end
for i in 0...$map_players.size
player = $other_players[$map_players[i]]
if player[2].to_i == @map_id
if @events[i+2000].nil? or @events[i+2000].erased
@events[i+2000] = Game_Event.new(@map_id, Map001.events[1])
@events[i+2000].erased = false
@events[i+2000].character_hue = rand(360)
@events[i+2000].moveto(player[0].to_i,player[1].to_i)
@events[i+2000].animation_id = 8
@events[i+2000].refresh
end
eve = @events[i+2000]
#eve.moveto($other_players[i][0].to_i,$other_players[i][1].to_i)
#p $other_players[i][3]
if eve.x != player[0].to_i
if eve.x < player[0].to_i
if eve.y != player[1].to_i
if eve.y < player[1].to_i
eve.move_lower_right
else
eve.move_upper_right
end
else
eve.move_right
end
else
if eve.y != player[1].to_i
if eve.y < player[1].to_i
eve.move_lower_left
else
eve.move_upper_left
end
else
eve.move_left
end
end
else
if eve.y != player[1].to_i
if eve.y < player[1].to_i
eve.move_down
else
eve.move_up
end
end
end
#@events[i+2000].x = $other_players[i][0].to_i
#@events[i+2000].y = $other_players[i][1].to_i
#else #不在当前地图
# @events[i+2000].erase if @events[i+2000] != nil
end #if
end #for
#p $other_players
#end
end #def
end #class
# ▼▲▼ TCP服务器映射 ▼▲▼
# by 零教授
#
#!需DLL和服务器支持!
#==============================================================================
# ■ Window_Base
#==============================================================================
$TCPput = Win32API.new('tcpforRMXP', 'tcpput', %w(i i i p), 'v')
$TCPget = Win32API.new('tcpforRMXP', 'tcpget', 'v','p')
$JOIN = Win32API.new('tcpforRMXP', 'join', 'v','v')
$Name = KOISHI::GetPrivateProfileString("Base","Name")
Map001 = load_datax(sprintf("Data/Map%03d.rxdata", 1))
$other_players = {}
$map_players = []
$old_x = 0
$old_y = 0
class Game_Map
alias setup_tcp setup
def setup(map_id)
setup_tcp(map_id)
#$map_players = []
# for i in 0...10
# @events[i+2000].erase if @events[i+2000] != nil
# end
#p $other_players
if $map_players.size > 0
for i in 0...$map_players.size
player = $other_players[$map_players[i]]
if player[2].to_i == map_id
#p $other_players[i][3]
@events[i+2000] = Game_Event.new(map_id, Map001.events[1])
@events[i+2000].erased = false
@events[i+2000].name = player[3]
@events[i+2000].character_hue = rand(360)
@events[i+2000].moveto(player[0].to_i,player[1].to_i)
@events[i+2000].refresh
#@events[i+2000].x = $other_players[i][0].to_i
#@events[i+2000].y = $other_players[i][1].to_i
#p @events[i+2000]
end
end
end
end
alias update_tcp update
def update
update_tcp
#if (Graphics.frame_count % 40) == 1
if $old_x != $game_player.x or $old_y != $game_player.y
$TCPput.call($game_player.x ,$game_player.y ,@map_id ,$Name)
$old_x, $old_y = $game_player.x, $game_player.y
end
begin
string = $TCPget.call
rescue
string = ""
end
# p string
tplayer = string.split(",")
if string != "" && tplayer.size == 5
if $other_players[tplayer[4]] == nil
$map_players.push(tplayer[4])
end
$other_players[tplayer[4]] = tplayer
if tplayer[2] == 0
$map_players.delete(tplayer[4])
$other_players[tplayer[4]] = nil
$game_temp.player_transferring = true
$game_temp.player_new_map_id = @map_id
$game_temp.player_new_x = $game_player.x
$game_temp.player_new_y = $game_player.y
$game_temp.player_new_direction = 5
$game_player_shock_flag = true
return
end
end
#p $other_players
#for i in 0..100-$other_players.size
# @events[2100-i] = nil
#end
for i in 0...$map_players.size
player = $other_players[$map_players[i]]
if player[2].to_i == @map_id
if @events[i+2000].nil? or @events[i+2000].erased
@events[i+2000] = Game_Event.new(@map_id, Map001.events[1])
@events[i+2000].erased = false
@events[i+2000].character_hue = rand(360)
@events[i+2000].moveto(player[0].to_i,player[1].to_i)
@events[i+2000].animation_id = 8
@events[i+2000].refresh
end
eve = @events[i+2000]
#eve.moveto($other_players[i][0].to_i,$other_players[i][1].to_i)
#p $other_players[i][3]
if eve.x != player[0].to_i
if eve.x < player[0].to_i
if eve.y != player[1].to_i
if eve.y < player[1].to_i
eve.move_lower_right
else
eve.move_upper_right
end
else
eve.move_right
end
else
if eve.y != player[1].to_i
if eve.y < player[1].to_i
eve.move_lower_left
else
eve.move_upper_left
end
else
eve.move_left
end
end
else
if eve.y != player[1].to_i
if eve.y < player[1].to_i
eve.move_down
else
eve.move_up
end
end
end
#@events[i+2000].x = $other_players[i][0].to_i
#@events[i+2000].y = $other_players[i][1].to_i
#else #不在当前地图
# @events[i+2000].erase if @events[i+2000] != nil
end #if
end #for
#p $other_players
#end
end #def
end #class
目前脚本和DLL都处于调试阶段,会出现诸多BUG
如下:
虽然效果上已经达到相当好的成果,但是是否能胜任网游的制作还有待考量
作者: 赤月抉择 时间: 2018-2-28 08:53
回复一下,
作者: huangke 时间: 2018-2-28 12:29
牛的一笔
作者: guoxiaomi 时间: 2018-2-28 12:44
如果有好的异步的tcp通讯就很棒了~其实我更感兴趣的是分辨率怎么扩大的?我写的联机,数据传输是走的http request而不是用tcp socket,但是客户端拿到数据后如何处理,这些是通用的,有兴趣可以去看看。
https://gitee.com/rmxp/rgss_server
作者: chd114 时间: 2018-2-28 14:19
图中的全部都是玩家?还是大部分是npc
如果其中一个玩家用了物品,使用物品的音效其他玩家可以听到吗?
作者: 零教授 时间: 2018-2-28 17:18
本帖最后由 零教授 于 2018-2-28 17:24 编辑
分辨率WIN32AI是用论坛的
光修改分辨率非常容易,但是要修改地图图块元件和人物元件,以及场景窗口就需要自适应
除开修改分辨率外还必须修改Scene_Battle、Spriteset_Battle、Scene_Map、Spriteset_Map、Game_Battler、Sprite_Battler、RPG::Weather这些主要的图片处理的类
#==============================================================================
# 本脚本来自[url]www.66RPG.com[/url],使用和转载请保留此信息
#==============================================================================
# ————————————————————————————————————
# 本脚本来自[url]www.66rpg.com[/url],转载请保留此信息
# ————————————————————————————————————
#ウィンドウサイズの変更(v1.00)
module THNDJ
GAME_INI_FILE = ".\\Game.ini"
#--------------------------------------------------------------------------
# ● 读取配置文件
#--------------------------------------------------------------------------
def THNDJ.GetPrivateProfileString(section, key)
val = "\0"*256
gps = Win32API.new('kernel32', 'GetPrivateProfileString',%w(p p p p l p), 'l')
gps.call(section, key, "", val, 256, GAME_INI_FILE)
val.delete!("\0")
return val
end
#--------------------------------------------------------------------------
# ● 写入配置文件
#--------------------------------------------------------------------------
def THNDJ.WritePrivateProfileString(section, key, val)
gps = Win32API.new('kernel32', 'WritePrivateProfileString',%w(p p p p), 'i')
return gps.call(section, key, val.to_s , GAME_INI_FILE)
end
#--------------------------------------------------------------------------
#--------------------------------------------------------------------------
def THNDJ.FindWindow(class_name, title)
fw = Win32API.new('user32', 'FindWindow', %(p, p), 'i')
hWnd = fw.call(class_name, title)
return hWnd
end
HWND_TOP = 0
HWND_TOPMOST = -1
SWP_NOMOVE = 0
#--------------------------------------------------------------------------
#--------------------------------------------------------------------------
def THNDJ.SetWindowPos(hWnd, w, h)
system_metrics = Win32API.new('user32', 'GetSystemMetrics', %(i), 'i')
sw = system_metrics.call(0)
sh = system_metrics.call(1)
# if sw <= 800 or sh <=600
# return false
# end
x_alias = (sw - w)/2
y_alias = (sh - h)/2
swp = Win32API.new('user32', 'SetWindowPos', %(l, l, i, i, i, i, i), 'i')
ok = swp.call(hWnd, HWND_TOP, x_alias, y_alias, w, h, SWP_NOMOVE)
# 下の行を有効にすると、常に前面に表示されるウィンドウになる。
#ok = swp.call(hWnd, HWND_TOPMOST, 0, 0, w, h, SWP_NOMOVE)
#SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags );
#参数1是在屏幕显示的层次优先级(最上最下);后四个决定窗口四个角的坐标;最后一个指定显示的状态。具体可以参阅MSDN.
return ok
end
end
title = THNDJ.GetPrivateProfileString("THNDJ", "NormalTitle")
$hWnd = THNDJ.FindWindow("RGSS Player", title)
def resize_window
# 幅320、高さ480に変更
$width = [[THNDJ.GetPrivateProfileString("THNDJ","$width").to_i,640].max,1280].min
$height = [[THNDJ.GetPrivateProfileString("THNDJ","$height").to_i,480].max,960].min
ok = THNDJ.SetWindowPos($hWnd, $width, $height)
$XRXS65AX = ($width-546)/2
$XRXS65AY = $height-90
if(ok == 0)
p "サイズ変更失敗"
$width = 640
$height = 480
$XRXS65AX = ($width-546)/2
$XRXS65AY = $height-90
return false
end
return true
end
module KOISHI
@player_id = THNDJ.GetPrivateProfileString("THNDJ", "player_id")
KOISHI_FILE = ".\\"+@player_id+".ini"
#--------------------------------------------------------------------------
# ● 读取配置文件
#--------------------------------------------------------------------------
def KOISHI.GetPrivateProfileString(section, key)
val = "\0"*256
gps = Win32API.new('kernel32', 'GetPrivateProfileString',%w(p p p p l p), 'l')
gps.call(section, key, "", val, 256, KOISHI_FILE)
val.delete!("\0")
return val
end
#--------------------------------------------------------------------------
# ● 写入配置文件
#--------------------------------------------------------------------------
def KOISHI.WritePrivateProfileString(section, key, val)
gps = Win32API.new('kernel32', 'WritePrivateProfileString',%w(p p p p), 'i')
return gps.call(section, key, val.to_s , KOISHI_FILE)
end
end
#==============================================================================
# 本脚本来自[url]www.66RPG.com[/url],使用和转载请保留此信息
#==============================================================================
#==============================================================================
# 本脚本来自[url]www.66RPG.com[/url],使用和转载请保留此信息
#==============================================================================
# ————————————————————————————————————
# 本脚本来自[url]www.66rpg.com[/url],转载请保留此信息
# ————————————————————————————————————
#ウィンドウサイズの変更(v1.00)
module THNDJ
GAME_INI_FILE = ".\\Game.ini"
#--------------------------------------------------------------------------
# ● 读取配置文件
#--------------------------------------------------------------------------
def THNDJ.GetPrivateProfileString(section, key)
val = "\0"*256
gps = Win32API.new('kernel32', 'GetPrivateProfileString',%w(p p p p l p), 'l')
gps.call(section, key, "", val, 256, GAME_INI_FILE)
val.delete!("\0")
return val
end
#--------------------------------------------------------------------------
# ● 写入配置文件
#--------------------------------------------------------------------------
def THNDJ.WritePrivateProfileString(section, key, val)
gps = Win32API.new('kernel32', 'WritePrivateProfileString',%w(p p p p), 'i')
return gps.call(section, key, val.to_s , GAME_INI_FILE)
end
#--------------------------------------------------------------------------
#--------------------------------------------------------------------------
def THNDJ.FindWindow(class_name, title)
fw = Win32API.new('user32', 'FindWindow', %(p, p), 'i')
hWnd = fw.call(class_name, title)
return hWnd
end
HWND_TOP = 0
HWND_TOPMOST = -1
SWP_NOMOVE = 0
#--------------------------------------------------------------------------
#--------------------------------------------------------------------------
def THNDJ.SetWindowPos(hWnd, w, h)
system_metrics = Win32API.new('user32', 'GetSystemMetrics', %(i), 'i')
sw = system_metrics.call(0)
sh = system_metrics.call(1)
# if sw <= 800 or sh <=600
# return false
# end
x_alias = (sw - w)/2
y_alias = (sh - h)/2
swp = Win32API.new('user32', 'SetWindowPos', %(l, l, i, i, i, i, i), 'i')
ok = swp.call(hWnd, HWND_TOP, x_alias, y_alias, w, h, SWP_NOMOVE)
# 下の行を有効にすると、常に前面に表示されるウィンドウになる。
#ok = swp.call(hWnd, HWND_TOPMOST, 0, 0, w, h, SWP_NOMOVE)
#SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags );
#参数1是在屏幕显示的层次优先级(最上最下);后四个决定窗口四个角的坐标;最后一个指定显示的状态。具体可以参阅MSDN.
return ok
end
end
title = THNDJ.GetPrivateProfileString("THNDJ", "NormalTitle")
$hWnd = THNDJ.FindWindow("RGSS Player", title)
def resize_window
# 幅320、高さ480に変更
$width = [[THNDJ.GetPrivateProfileString("THNDJ","$width").to_i,640].max,1280].min
$height = [[THNDJ.GetPrivateProfileString("THNDJ","$height").to_i,480].max,960].min
ok = THNDJ.SetWindowPos($hWnd, $width, $height)
$XRXS65AX = ($width-546)/2
$XRXS65AY = $height-90
if(ok == 0)
p "サイズ変更失敗"
$width = 640
$height = 480
$XRXS65AX = ($width-546)/2
$XRXS65AY = $height-90
return false
end
return true
end
module KOISHI
@player_id = THNDJ.GetPrivateProfileString("THNDJ", "player_id")
KOISHI_FILE = ".\\"+@player_id+".ini"
#--------------------------------------------------------------------------
# ● 读取配置文件
#--------------------------------------------------------------------------
def KOISHI.GetPrivateProfileString(section, key)
val = "\0"*256
gps = Win32API.new('kernel32', 'GetPrivateProfileString',%w(p p p p l p), 'l')
gps.call(section, key, "", val, 256, KOISHI_FILE)
val.delete!("\0")
return val
end
#--------------------------------------------------------------------------
# ● 写入配置文件
#--------------------------------------------------------------------------
def KOISHI.WritePrivateProfileString(section, key, val)
gps = Win32API.new('kernel32', 'WritePrivateProfileString',%w(p p p p), 'i')
return gps.call(section, key, val.to_s , KOISHI_FILE)
end
end
#==============================================================================
# 本脚本来自[url]www.66RPG.com[/url],使用和转载请保留此信息
#==============================================================================
作者: WantMy蕙 时间: 2018-2-28 19:11
联机其实这个想法很多人都有想过
也有人付诸过实际行动
但是这方面最大的问题就在于服务器
有谁会愿意做一个免费的RM去开服务器给大家玩呢???
现在服务器又那么贵
除非是自己的经济来源来自于机房,然后可以分一小块,供RM使用
要不就得用RM做高品质的游戏,来吸引玩家一次性购买/内购
不过RM做高品质限制还是很多的
话说回来楼主还是 挺厉害的
加油吧
作者: fux2 时间: 2018-2-28 23:24
如果郭兄有需要我这里倒是可以糊一个突破限制的tilemap
作者: 574656549 时间: 2018-3-19 18:09
提示: 作者被禁止或删除 内容自动屏蔽
作者: ikki 时间: 2018-3-24 15:15
10多年前确实有联网的dll出现,并且连接成功了,不过,后来就没有进一步进展了,那个实现的是在线走路和聊天对话,没完成在线组队打怪。
欢迎光临 Project1 (https://rpg.blue/) |
Powered by Discuz! X3.1 |