Added necessary js type exposure to jsexport.

This commit is contained in:
genxium
2022-12-24 13:57:32 +08:00
parent df5c9fda30
commit 8a9d449d83
11 changed files with 1779 additions and 78 deletions

View File

@@ -1,51 +1,100 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
. "dnmshared"
. "dnmshared/sharedprotos"
"github.com/gopherjs/gopherjs/js"
"github.com/solarlune/resolv"
. "jsexport/protos"
"jsexport/models"
. "dnmshared"
"jsexport/models"
. "jsexport/protos"
)
func NewRingBufferJs(n int32) *js.Object {
return js.MakeWrapper(NewRingBuffer(n));
}
func NewCollisionSpaceJs(spaceW, spaceH, minStepW, minStepH int) *js.Object {
return js.MakeWrapper(resolv.NewSpace(spaceW, spaceH, minStepW, minStepH))
return js.MakeWrapper(resolv.NewSpace(spaceW, spaceH, minStepW, minStepH))
}
func GenerateRectColliderJs(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, tag string) *js.Object {
/*
[WARNING] It's important to note that we don't need "js.MakeFullWrapper" for a call sequence as follows.
```
var space = gopkgs.NewCollisionSpaceJs(2048, 2048, 8, 8);
var a = gopkgs.GenerateRectColliderJs(189, 497, 48, 48, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, spaceOffsetX, spaceOffsetY, "Player");
space.Add(a);
```
The "space" variable doesn't need access to the field of "a" in JavaScript level to run "space.Add(...)" method, which is good.
*/
return js.MakeWrapper(GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, tag));
func NewVec2DJs(x, y float64) *js.Object {
return js.MakeFullWrapper(&Vec2D{
X: x,
Y: y,
})
}
func NewPolygon2DJs(anchor *Vec2D, points []*Vec2D) *js.Object {
return js.MakeFullWrapper(&Polygon2D{
Anchor: anchor,
Points: points,
})
}
func NewBarrierJs(boundary *Polygon2D) *js.Object {
return js.MakeWrapper(&Barrier{
Boundary: boundary,
})
}
func NewPlayerDownsyncJs(id, virtualGridX, virtualGridY, dirX, dirY, velX, velY, speed, battleState, characterState, joinIndex, hp, maxHp int32, inAir bool, colliderRadius float64) *js.Object {
return js.MakeWrapper(&PlayerDownsync{
Id: id,
VirtualGridX: virtualGridX,
VirtualGridY: virtualGridY,
DirX: dirX,
DirY: dirY,
VelX: velX,
VelY: velY,
Speed: speed,
BattleState: battleState,
JoinIndex: joinIndex,
ColliderRadius: colliderRadius,
Hp: hp,
MaxHp: maxHp,
CharacterState: characterState,
InAir: inAir,
})
}
func NewRoomDownsyncFrameJs(id int32, playersArr []*PlayerDownsync, meleeBullets []*MeleeBullet) *js.Object {
return js.MakeFullWrapper(&RoomDownsyncFrame{
Id: id,
PlayersArr: playersArr,
MeleeBullets: meleeBullets,
})
}
func GenerateRectColliderJs(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *js.Object {
/*
[WARNING] It's important to note that we don't need "js.MakeFullWrapper" for a call sequence as follows.
```
var space = gopkgs.NewCollisionSpaceJs(2048, 2048, 8, 8);
var a = gopkgs.GenerateRectColliderJs(189, 497, 48, 48, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, spaceOffsetX, spaceOffsetY, "Player");
space.Add(a);
```
The "space" variable doesn't need access to the field of "a" in JavaScript level to run "space.Add(...)" method, which is good.
*/
return js.MakeWrapper(GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, data, tag))
}
func CheckCollisionJs(obj *resolv.Object, dx, dy float64) *js.Object {
// TODO: Support multiple tags in the future
// Unfortunately I couldn't find a way to just call "var a = GenerateRectColliderJs(...); space.Add(a); a.Check(...)" to get the collision result, the unwrapped method will result in stack overflow. Need a better solution later.
return js.MakeFullWrapper(obj.Check(dx, dy));
// TODO: Support multiple tags in the future
// Unfortunately I couldn't find a way to just call "var a = GenerateRectColliderJs(...); space.Add(a); a.Check(...)" to get the collision result, the unwrapped method will result in stack overflow. Need a better solution later.
return js.MakeFullWrapper(obj.Check(dx, dy))
}
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(delayedInputFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames int32, inputsBuffer *RingBuffer, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64) *js.Object {
// We need access to all fields of RoomDownsyncFrame for displaying in frontend
return js.MakeFullWrapper(models.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames, inputsBuffer, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio))
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(delayedInputFrame, delayedInputFrameForPrevRenderFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames int32, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64) *js.Object {
// We need access to all fields of RoomDownsyncFrame for displaying in frontend
return js.MakeFullWrapper(models.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, delayedInputFrameForPrevRenderFrame, currRenderFrame, collisionSys, collisionSysMap, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio))
}
func main() {
js.Global.Set("gopkgs", map[string]interface{}{
"NewRingBufferJs": NewRingBufferJs,
"NewCollisionSpaceJs": NewCollisionSpaceJs,
"GenerateRectColliderJs": GenerateRectColliderJs,
"CheckCollisionJs": CheckCollisionJs,
"NewVec2DJs": NewVec2DJs,
"NewPolygon2DJs": NewPolygon2DJs,
"NewBarrierJs": NewBarrierJs,
"NewPlayerDownsyncJs": NewPlayerDownsyncJs,
"NewRoomDownsyncFrameJs": NewRoomDownsyncFrameJs,
"NewCollisionSpaceJs": NewCollisionSpaceJs,
"GenerateRectColliderJs": GenerateRectColliderJs,
"CheckCollisionJs": CheckCollisionJs,
})
}

View File

@@ -1,10 +1,10 @@
package models
import (
. "dnmshared"
. "dnmshared/sharedprotos"
"github.com/solarlune/resolv"
. "dnmshared/sharedprotos"
. "jsexport/protos"
. "dnmshared"
. "jsexport/protos"
)
const (
@@ -81,10 +81,10 @@ func CalcHardPushbacksNorms(playerCollider *resolv.Object, playerShape *resolv.C
return ret
}
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames int32, inputsBuffer *RingBuffer, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64) *RoomDownsyncFrame {
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, delayedInputFrameForPrevRenderFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames int32, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64) *RoomDownsyncFrame {
topPadding, bottomPadding, leftPadding, rightPadding := snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap
// [WARNING] This function MUST BE called while "InputsBufferLock" is locked!
roomCapacity := len(currRenderFrame.PlayersArr)
roomCapacity := len(currRenderFrame.PlayersArr)
nextRenderFramePlayers := make([]*PlayerDownsync, roomCapacity)
// Make a copy first
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
@@ -117,11 +117,6 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *Input
// 1. Process player inputs
if nil != delayedInputFrame {
var delayedInputFrameForPrevRenderFrame *InputFrameDownsync = nil
tmp := inputsBuffer.GetByFrameId(ConvertToInputFrameId(currRenderFrame.Id-1, inputDelayFrames, inputScaleFrames))
if nil != tmp {
delayedInputFrameForPrevRenderFrame = tmp.(*InputFrameDownsync)
}
inputList := delayedInputFrame.InputList
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
@@ -150,16 +145,16 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *Input
}
}
// Note that by now "0 == thatPlayerInNextFrame.FramesToRecover", we should change "CharacterState" to "WALKING" or "IDLE" depending on player inputs
if 0 != decodedInput.Dx || 0 != decodedInput.Dy {
thatPlayerInNextFrame.DirX = decodedInput.Dx
thatPlayerInNextFrame.DirY = decodedInput.Dy
thatPlayerInNextFrame.VelX = decodedInput.Dx * currPlayerDownsync.Speed
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_WALKING
} else {
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
thatPlayerInNextFrame.VelX = 0
}
// Note that by now "0 == thatPlayerInNextFrame.FramesToRecover", we should change "CharacterState" to "WALKING" or "IDLE" depending on player inputs
if 0 != decodedInput.Dx || 0 != decodedInput.Dy {
thatPlayerInNextFrame.DirX = decodedInput.Dx
thatPlayerInNextFrame.DirY = decodedInput.Dy
thatPlayerInNextFrame.VelX = decodedInput.Dx * currPlayerDownsync.Speed
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_WALKING
} else {
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
thatPlayerInNextFrame.VelX = 0
}
}
}
@@ -187,9 +182,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *Input
}
}
// 3. Invoke collision system stepping (no-op for backend collision lib)
// 4. Calc pushbacks for each player (after its movement) w/o bullets
// 3. Calc pushbacks for each player (after its movement) w/o bullets
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
@@ -201,6 +194,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *Input
if collision := playerCollider.Check(0, 0); nil != collision {
for _, obj := range collision.Objects {
isBarrier, isAnotherPlayer, isBullet := false, false, false
// TODO: Make this part work in JavaScript without having to expose all types Barrier/PlayerDownsync/MeleeBullet by js.MakeWrapper.
switch obj.Data.(type) {
case *Barrier:
isBarrier = true
@@ -226,7 +220,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *Input
thatPlayerInNextFrame.InAir = false
}
if isAnotherPlayer {
// [WARNING] The "zero overlap collision" might be randomly detected/missed on either frontend or backend, to have deterministic result we added paddings to all sides of a playerCollider. As each velocity component of (velX, velY) being a multiple of 0.5 at any renderFrame, each position component of (x, y) can only be a multiple of 0.5 too, thus whenever a 1-dimensional collision happens between players from [player#1: i*0.5, player#2: j*0.5, not collided yet] to [player#1: (i+k)*0.5, player#2: j*0.5, collided], the overlap becomes (i+k-j)*0.5+2*s, and after snapping subtraction the effPushback magnitude for each player is (i+k-j)*0.5, resulting in 0.5-multiples-position for the next renderFrame.
// [WARNING] The "zero overlap collision" might be randomly detected/missed on either frontend or backend, to have deterministic result we added paddings to all sides of a playerCollider. As each velocity component of (velX, velY) being a multiple of 0.5 at any renderFrame, each position component of (x, y) can only be a multiple of 0.5 too, thus whenever a 1-dimensional collision happens between players from [player#1: i*0.5, player#2: j*0.5, not collided yet] to [player#1: (i+k)*0.5, player#2: j*0.5, collided], the overlap becomes (i+k-j)*0.5+2*s, and after snapping subtraction the effPushback magnitude for each player is (i+k-j)*0.5, resulting in 0.5-multiples-position for the next renderFrame.
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap*2)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap*2)*overlapResult.OverlapY
}
for _, hardPushbackNorm := range hardPushbackNorms[joinIndex-1] {
@@ -262,7 +256,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *Input
}
}
// 7. Get players out of stuck barriers if there's any
// 4. Get players out of stuck barriers if there's any
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
@@ -274,7 +268,7 @@ func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *Input
}
return &RoomDownsyncFrame{
Id: currRenderFrame.Id + 1,
PlayersArr: nextRenderFramePlayers,
Id: currRenderFrame.Id + 1,
PlayersArr: nextRenderFramePlayers,
}
}