|
楼主 |
发表于 2008-1-25 22:42:00
|
显示全部楼层
原来那个填坑帖实在太长了,我还是在这个帖整理下RPBUG
==================================朴素的分割线=============================
序:
科学无法解释的问题,我们称之为RP问题。
今天发布电子书,征集书中的BUG;随后有朋友给我消息,说Return Bug+GameCache的应用(一)中,老狼的代码加入图中会导致崩溃,于是,我看了下这段代码:
[codes=jass]
function H2I takes handle h returns integer
return h
return 0
endfunction
function I2TC takes integer i returns triggercondition
return i
return null
endfunction
function I2TG takes integer i returns trigger
return i
return null
endfunction
function DestroyTriggerAllById takes integer t returns nothing
call TriggerRemoveCondition(I2TG(t),I2TC(GetStoredInteger (udg_GC,I2S(t),"TriggerCondition")))
call DestroyTrigger(I2TG(t))
call FlushStoredMission(udg_GC,I2S(t))
endfunction
function DestroyTriggerAll takes trigger trg returns nothing
call TriggerRemoveCondition(trg,I2TC(GetStoredInteger(udg_GC,I2S(H2I(trg)),"TriggerCondition")))
call DestroyTrigger(trg)
call FlushStoredMission(udg_GC,I2S(H2I(trg)))
endfunction
//========================================================
function RegisterUnitAmortCond takes nothing returns nothing
call SetUnitInvulnerable(GetTriggerUnit(), true)
call DestroyTriggerAll(GetTriggeringTrigger())
endfunction
function RegisterUnitAmortEvent takes unit witchUnit returns nothing
local trigger trg = CreateTrigger()
call TriggerRegisterUnitStateEvent(trg, witchUnit, UNIT_STATE_LIFE, LESS_THAN_OR_EQUAL, 50)
call StoreInteger(udg_GC,I2S(H2I(trg)),"TriggerCondition",H2I (TriggerAddCondition(trg,Condition(function RegisterUnitAmortCond))))
set trg = null
endfunction[/codes]
这段代码横看竖看都没问题,于是我把它加进地图,果然,弹出来了.于是我暂时把代码中的条件换成了动作
[codes=jass]
function H2I takes handle h returns integer
return h
return 0
endfunction
function I2TA takes integer i returns triggeraction
return i
return null
endfunction
function I2TG takes integer i returns trigger
return i
return null
endfunction
function DestroyTriggerAllById takes integer t returns nothing
call TriggerRemoveAction(I2TG(t),I2TA(GetStoredInteger (udg_GC,I2S(t),"Triggeraction")))
call DestroyTrigger(I2TG(t))
call FlushStoredMission(udg_GC,I2S(t))
endfunction
function DestroyTriggerAll takes trigger trg returns nothing
call TriggerRemoveAction(trg,I2TA(GetStoredInteger(udg_GC,I2S(H2I(trg)),"Triggeraction")))
call DestroyTrigger(trg)
call FlushStoredMission(udg_GC,I2S(H2I(trg)))
endfunction
//========================================================
function RegisterUnitAmortAction takes nothing returns nothing
call SetUnitInvulnerable(GetTriggerUnit(), true)
call DestroyTriggerAll(GetTriggeringTrigger())
endfunction
function RegisterUnitAmortEvent takes unit witchUnit returns nothing
local trigger trg = CreateTrigger()
call TriggerRegisterUnitStateEvent(trg, witchUnit, UNIT_STATE_LIFE, LESS_THAN_OR_EQUAL, 50)
call StoreInteger(udg_GC,I2S(H2I(trg)),"Triggeraction",H2I (TriggerAddAction(trg,function RegisterUnitAmortAction)))
set trg = null
endfunction[/codes]
运行下,没有问题;因此朋友交给我的任务完成了.不过为何用动作没有问题,而用条件会弹出游戏呢.经过尝试,发现个非常有趣的BUG,大家看这行代码
[codes=jass]
function H2I takes handle h returns integer
return h
return 0
endfunction
function I2TC takes integer i returns triggercondition
return i
return null
endfunction
function I2TG takes integer i returns trigger
return i
return null
endfunction
function DestroyTriggerAllById takes integer t returns nothing
call TriggerRemoveCondition(I2TG(t),I2TC(GetStoredInteger (udg_GC,I2S(t),"TriggerCondition")))
call DestroyTrigger(I2TG(t))
call FlushStoredMission(udg_GC,I2S(t))
endfunction
function DestroyTriggerAll takes trigger trg returns nothing
call TriggerRemoveCondition(trg,I2TC(GetStoredInteger(udg_GC,I2S(H2I(trg)),"TriggerCondition")))
call DestroyTrigger(trg)
call FlushStoredMission(udg_GC,I2S(H2I(trg)))
call BJDebugMsg(I2S(0))
endfunction
//========================================================
function RegisterUnitAmortCond takes nothing returns nothing
call SetUnitInvulnerable(GetTriggerUnit(), true)
call DestroyTriggerAll(GetTriggeringTrigger())
endfunction
function RegisterUnitAmortEvent takes unit witchUnit returns nothing
local trigger trg = CreateTrigger()
call TriggerRegisterUnitStateEvent(trg, witchUnit, UNIT_STATE_LIFE, LESS_THAN_OR_EQUAL, 50)
call StoreInteger(udg_GC,I2S(H2I(trg)),"TriggerCondition",H2I (TriggerAddCondition(trg,Condition(function RegisterUnitAmortCond))))
set trg = null
endfunction[/codes]
大家看这段代码有什么不同,无非是加了段call BJDebugMsg(I2S(0)),这个纠错函数,其实就是显示个字符在屏幕上,并不会影响程序运行,但不可思议的事发生了,这段代码加入后,游戏不会弹出了.
或许你还没明白我在说什么,我想说的是,将一个语法上百分百没有错误的代码,加入到地图后百分百会弹出,但加了个百分百没有用的call BJDebugMsg(I2S(0))后,游戏就不会出错.
是不是一个很有趣的BUG~
放上演示,大家不妨试试,去掉代码中的call BJDebugMsg(I2S(0)),就会弹出游戏。
未知BUG.w3x
(18 KB, 下载次数: 6)
那么,为何老狼的代码会弹出游戏,而把条件换成动作就不会出错,feelerly给出了一个比较好的回答——“在条件动作中不能删除条件”:
“今天做了很多测试,基本上搞懂了出错的原因:在同个触发器中,不能直接用条件动作来删除该条件。 否则会出错(这个有点奇怪,删除触发器没有事,删条件就有事了)
根据我的分析, 应该是条件动作没有返回真假值之前,一直处于运行状态,如果直接在没有返回真假值之前就给触发器移除条件,这就相当于你在吃饼的时候,突然咽住了一样(就是内存冲突)
所以,我们必须在条件返回真假值之后(就是用计时器延时删除)这样就不会出错了。”以下是feelerly给出的代码:
[codes=jass]function ConvertHandleInt takes handle h returns integer
return h
return 0
endfunction
function ConvertTrigger takes integer i returns trigger
return i
return null
endfunction
function ConvertCondition takes integer i returns triggercondition
return i
return null
endfunction
function TimerDestroyTriggerAll takes nothing returns nothing
local integer c = GetStoredInteger(bj_lastCreatedGameCache, "T","C")
local integer t = GetStoredInteger(bj_lastCreatedGameCache, I2S(ConvertHandleInt(GetExpiredTimer())),"T")
call TriggerRemoveCondition(ConvertTrigger(t), ConvertCondition(c) )
call DestroyTrigger( ConvertTrigger(t) )
call FlushStoredMission(bj_lastCreatedGameCache, I2S(ConvertHandleInt(GetExpiredTimer())))
call FlushStoredMission(bj_lastCreatedGameCache, "T")
call DestroyTimer( GetExpiredTimer() )
endfunction
function Trig_T_Conditions takes nothing returns boolean
local timer t = CreateTimer()
call FogEnable( false )
call FogMaskEnable( false )
call DisplayTextToPlayer( Player(0), 0,0, "123")
call StoreInteger( bj_lastCreatedGameCache,I2S(ConvertHandleInt(t)),"T",ConvertHandleInt(GetTriggeringTrigger()))
call TimerStart( t, 0.05, false, function TimerDestroyTriggerAll )
return true
endfunction
//===========================================================================
function InitTrig_T takes nothing returns nothing
local integer i = 0
set bj_lastCreatedGameCache=InitGameCache("test.m3v")
set gg_trg_T = CreateTrigger( )
call TriggerRegisterPlayerEventEndCinematic( gg_trg_T, Player(0) )
set i = ConvertHandleInt(TriggerAddCondition( gg_trg_T, Condition(function Trig_T_Conditions) ))
call StoreInteger( bj_lastCreatedGameCache, "T","C", i )
endfunction[/codes]
我补充下,为何“删除触发器没有事,删条件就有事”:因为触发器即便被销毁,也会执行一次;同样的道理,在触发器动作上也适用。
此外,feelerly的方法,利用到了call与Timer线程的不同。 call 函数的执行是窜行的,而触发器、Timer和ExecuteFunc等执行是并行的;因此,call 函数中执行的函数,也被看做是条件中的一部分,因此这函数中销毁条件,实际是在条件中销毁条件, 条件在没有运行完前就被销毁。
但使用Timer(其实也可用ExecuteFunc)后,线程关系是并行的,因此这样删除条件,Timer或ExecuteFunc执行的函数,不隶属于条件,它是在条件的外部销毁条件,所以不会出错。
不过,使用ExecuteFunc也会出错,feelerly猜测ExecuteFunc仍会保持那个进程中的数。
那么,到目前为止,我们解决这个RPBug了么——没有,相反才刚开始。
下面是zhezhutou提供的演示,在这个演示中,当最后一个农民生命值小于50后,就会弹出游戏
rpbug.w3x
(19 KB, 下载次数: 6)
为何要执行到最后一个农民,程序才会崩溃呢,feelerly说这可能跟JASS的编译有关;目前这个问题仍是无解,我也很想知道为何在老狼代码中加个无用的函数能阻止程序的崩溃。
很抱歉,身为前辈的我们无法解决这问题,但解决它是你们的目标和任务;如果能解决这BUG,那我们对JASS的认识可以更深一步。 |
|