github.com/metaworking/channeld@v0.7.3/pkg/unreal/message.go (about) 1 package unreal 2 3 import ( 4 "github.com/metaworking/channeld/pkg/channeld" 5 "github.com/metaworking/channeld/pkg/channeldpb" 6 "github.com/metaworking/channeld/pkg/common" 7 "github.com/metaworking/channeld/pkg/unrealpb" 8 "go.uber.org/zap" 9 "google.golang.org/protobuf/proto" 10 ) 11 12 func InitMessageHandlers() { 13 channeld.RegisterMessageHandler(uint32(unrealpb.MessageType_SPAWN), &channeldpb.ServerForwardMessage{}, handleUnrealSpawnObject) 14 channeld.RegisterMessageHandler(uint32(unrealpb.MessageType_DESTROY), &channeldpb.ServerForwardMessage{}, handleUnrealDestroyObject) 15 } 16 17 // Executed in the spatial channels or the GLOBAL channel (no-spatial scenario) 18 func handleUnrealSpawnObject(ctx channeld.MessageContext) { 19 // server -> channeld -> client 20 msg, ok := ctx.Msg.(*channeldpb.ServerForwardMessage) 21 if !ok { 22 ctx.Connection.Logger().Error("message is not a ServerForwardMessage, will not be handled.") 23 return 24 } 25 26 spawnMsg := &unrealpb.SpawnObjectMessage{} 27 err := proto.Unmarshal(msg.Payload, spawnMsg) 28 if err != nil { 29 ctx.Connection.Logger().Error("failed to unmarshal SpawnObjectMessage") 30 return 31 } 32 33 if spawnMsg.Obj == nil { 34 ctx.Connection.Logger().Error("SpawnObjectMessage doesn't have the 'Obj' field") 35 return 36 } 37 38 if spawnMsg.Obj.NetGUID == nil || *spawnMsg.Obj.NetGUID == 0 { 39 ctx.Connection.Logger().Error("invalid NetGUID in SpawnObjectMessage") 40 return 41 } 42 43 /* 44 if len(spawnMsg.Obj.Context) == 0 { 45 ctx.Connection.Logger().Warn("empty context in SpawnObjectMessage", zap.Uint32("netId", *spawnMsg.Obj.NetGUID)) 46 } 47 */ 48 49 // Update the message's spatial channelId based on the actor's location 50 oldChId := *spawnMsg.ChannelId 51 if spawnMsg.Location != nil { 52 // Swap the Y and Z as UE uses the Z-Up rule but channeld uses the Y-up rule. 53 spatialChId, err := channeld.GetSpatialController().GetChannelId(common.SpatialInfo{ 54 X: float64(*spawnMsg.Location.X), 55 Y: float64(*spawnMsg.Location.Z), 56 Z: float64(*spawnMsg.Location.Y), 57 }) 58 if err != nil { 59 ctx.Connection.Logger().Warn("failed to GetChannelId", zap.Error(err), 60 zap.Float32("x", *spawnMsg.Location.X), 61 zap.Float32("y", *spawnMsg.Location.Y), 62 zap.Float32("z", *spawnMsg.Location.Z)) 63 return 64 } 65 *spawnMsg.ChannelId = uint32(spatialChId) 66 if *spawnMsg.ChannelId != oldChId { 67 newPayload, err := proto.Marshal(spawnMsg) 68 if err == nil { 69 msg.Payload = newPayload 70 // Update the channel and let the new channel handle the message. Otherwise race conditions may happen. 71 ctx.Channel = channeld.GetChannel(spatialChId) 72 if ctx.Channel != nil { 73 ctx.Channel.Execute(func(ch *channeld.Channel) { 74 addSpatialEntity(ch, spawnMsg.Obj) 75 }) 76 ctx.Channel.PutMessageContext(ctx, channeld.HandleServerToClientUserMessage) 77 } else { 78 ctx.Connection.Logger().Error("failed to handle the ServerForwardMessage as the new spatial channel doesn't exist", zap.Uint32("newChId", *spawnMsg.ChannelId)) 79 } 80 } else { 81 ctx.Connection.Logger().Error("failed to marshal the new payload") 82 } 83 } else { 84 // ChannelId is not updated; handle the forward message in current channel. 85 addSpatialEntity(ctx.Channel, spawnMsg.Obj) 86 channeld.HandleServerToClientUserMessage(ctx) 87 } 88 } else { 89 addSpatialEntity(ctx.Channel, spawnMsg.Obj) 90 channeld.HandleServerToClientUserMessage(ctx) 91 } 92 93 /* 94 defer allSpawnedObjLock.Unlock() 95 allSpawnedObjLock.Lock() 96 allSpawnedObj[*spawnMsg.Obj.NetGUID] = spawnMsg.Obj 97 channeld.RootLogger().Debug("stored UnrealObjectRef from spawn message", 98 zap.Uint32("netId", *spawnMsg.Obj.NetGUID), 99 zap.Uint32("oldChId", oldChId), 100 zap.Uint32("newChId", *spawnMsg.ChannelId), 101 ) 102 */ 103 104 // Entity channel should already be created by the spatial server. 105 entityChannel := channeld.GetChannel(common.ChannelId(*spawnMsg.Obj.NetGUID)) 106 if entityChannel == nil { 107 return 108 } 109 110 // Set the objRef of the entity channel's data 111 entityChannel.Execute(func(ch *channeld.Channel) { 112 if entityData, ok := ch.GetDataMessage().(UnrealObjectEntityData); ok { 113 entityData.SetObjRef(spawnMsg.Obj) 114 ch.Logger().Debug("set entity data's objRef") 115 } 116 }) 117 } 118 119 // Entity channel data that contains an UnrealObjectRef should implement this interface. 120 type UnrealObjectEntityData interface { 121 SetObjRef(objRef *unrealpb.UnrealObjectRef) 122 } 123 124 func addSpatialEntity(ch *channeld.Channel, objRef *unrealpb.UnrealObjectRef) { 125 if ch.Type() != channeldpb.ChannelType_SPATIAL { 126 return 127 } 128 129 if ch.GetDataMessage() == nil { 130 return 131 } 132 133 spatialChannelData, ok := ch.GetDataMessage().(*unrealpb.SpatialChannelData) 134 if !ok { 135 ch.Logger().Warn("channel data is not a SpatialChannelData", 136 zap.String("dataType", string(ch.GetDataMessage().ProtoReflect().Descriptor().FullName()))) 137 return 138 } 139 140 entityState := &unrealpb.SpatialEntityState{ObjRef: &unrealpb.UnrealObjectRef{}} 141 proto.Merge(entityState.ObjRef, objRef) 142 spatialChannelData.Entities[*objRef.NetGUID] = entityState 143 ch.Logger().Debug("added spatial entity", zap.Uint32("netId", *objRef.NetGUID)) 144 } 145 146 func removeSpatialEntity(ch *channeld.Channel, netId uint32) { 147 if ch.Type() != channeldpb.ChannelType_SPATIAL { 148 return 149 } 150 151 if ch.GetDataMessage() == nil { 152 return 153 } 154 155 spatialChannelData, ok := ch.GetDataMessage().(*unrealpb.SpatialChannelData) 156 if !ok { 157 ch.Logger().Warn("channel data is not a SpatialChannelData", 158 zap.String("dataType", string(ch.GetDataMessage().ProtoReflect().Descriptor().FullName()))) 159 return 160 } 161 162 delete(spatialChannelData.Entities, netId) 163 ch.Logger().Debug("removed spatial entity", zap.Uint32("netId", netId)) 164 } 165 166 func handleUnrealDestroyObject(ctx channeld.MessageContext) { 167 // server -> channeld -> client 168 msg, ok := ctx.Msg.(*channeldpb.ServerForwardMessage) 169 if !ok { 170 ctx.Connection.Logger().Error("message is not a ServerForwardMessage, will not be handled.") 171 return 172 } 173 174 destroyMsg := &unrealpb.DestroyObjectMessage{} 175 err := proto.Unmarshal(msg.Payload, destroyMsg) 176 if err != nil { 177 ctx.Connection.Logger().Error("failed to unmarshal DestroyObjectMessage") 178 return 179 } 180 181 removeSpatialEntity(ctx.Channel, destroyMsg.NetId) 182 // Send/broadcast the message 183 channeld.HandleServerToClientUserMessage(ctx) 184 185 entityCh := channeld.GetChannel(common.ChannelId(destroyMsg.NetId)) 186 if entityCh != nil { 187 entityCh.Logger().Info("removing entity channel from unrealpb.DestroyObjectMessage") 188 channeld.RemoveChannel(entityCh) 189 } 190 }