mirror of
https://github.com/PretendoNetwork/nex-protocols-common-go.git
synced 2025-04-02 11:02:13 -04:00
Compare commits
192 commits
Author | SHA1 | Date | |
---|---|---|---|
|
db917c2172 | ||
|
52d289e761 | ||
|
9005d07605 | ||
|
0fe4724b02 | ||
|
ffaba6e616 | ||
|
1450b7ffcc | ||
|
f83d9061ee | ||
|
59c7633654 | ||
|
63aaefe721 | ||
|
7781eb9e7b | ||
|
fa8845a22b | ||
|
7ba13e1abc | ||
|
2fd1f78759 | ||
|
426a8a81dc | ||
|
a01073f4e9 | ||
|
d5a4a74726 | ||
|
35aed21585 | ||
|
8084bcdf96 | ||
|
2b1d5b7c9b | ||
|
bcc53deac2 | ||
|
7f067a8ccd | ||
|
f9e65db077 | ||
|
5651e7f651 | ||
|
21fdc4730e | ||
|
d7f8b585c1 | ||
|
f7a2dab7cc | ||
|
9a67d7e82c | ||
|
9696be334e | ||
|
7b42e6a794 | ||
|
dd5156d92b | ||
|
e853c5df55 | ||
|
16ed5f16db | ||
|
b7042f3435 | ||
|
80765de633 | ||
|
0290f5c994 | ||
|
165afdc00d | ||
|
32a375d64e | ||
|
808a786461 | ||
|
69e3b1f342 | ||
|
32d393f708 | ||
|
ff20935c8c | ||
|
9639ab6c06 | ||
|
6953e78d24 | ||
|
dfe34baf48 | ||
|
8caa52cbbe | ||
|
14ae73e15e | ||
|
20f418e04f | ||
|
ed1f78ccfa | ||
|
9ccdfc9704 | ||
|
cc89c2ed3f | ||
|
03912fde5b | ||
|
54f171f298 | ||
|
e71f872ac1 | ||
|
98a2806b0c | ||
|
5ab8299563 | ||
|
e1957bbca9 | ||
|
06dc3fbc05 | ||
|
66a942a388 | ||
|
b169655951 | ||
|
785f021ffa | ||
|
3ca998f820 | ||
|
8e8e2bdbeb | ||
|
1fd823034b | ||
|
95a9481cc9 | ||
|
b782903fd9 | ||
|
d435690ad0 | ||
|
112d27947c | ||
|
dfe7dba66b | ||
|
67fb7c2dd3 | ||
|
e4fcbbbd97 | ||
|
469c77a247 | ||
|
68d4aa6873 | ||
|
8208e97bc2 | ||
|
656fa0fafb | ||
|
34f42e501d | ||
|
93a6c5d0f5 | ||
|
cef643aed0 | ||
|
a8cc302e32 | ||
|
3d22887dc8 | ||
|
4c2b1506be | ||
|
7f33824207 | ||
|
9ef1a27cf2 | ||
|
2728968441 | ||
|
87d7dc43b6 | ||
|
a97b74843a | ||
|
b6c8028476 | ||
|
410ca40c01 | ||
|
bbb4872e89 | ||
|
d15b82cc12 | ||
|
2f6d8445c6 | ||
|
ce83fe9720 | ||
|
33c6ccc225 | ||
|
6fab5521a7 | ||
|
6aa24a9ccf | ||
|
1a22244675 | ||
|
5168b5c3d4 | ||
|
bca1304ade | ||
|
2f18d58d87 | ||
|
a00ab245bf | ||
|
a5300196f8 | ||
|
365fe3f1e3 | ||
|
612033c7ee | ||
|
63102b9e20 | ||
|
aab72d6f17 | ||
|
c8ca7cab4a | ||
|
1836be6eb6 | ||
|
cf7a3ef105 | ||
|
f3f8c3b3e5 | ||
|
b03ebf8adb | ||
|
116149213d | ||
|
161f95da5f | ||
|
4162f6c9ac | ||
|
202487c7c7 | ||
|
5dd7310eda | ||
|
3ac05a7067 | ||
|
7e6bd47ff4 | ||
|
dc4ca3547b | ||
|
0e39f84cf9 | ||
|
045afaab5b | ||
|
edcc444a36 | ||
|
8acdf1c23d | ||
|
6404c51293 | ||
|
011d7a1753 | ||
|
2a06105c8d | ||
|
0df41b9a21 | ||
|
63149ad6f6 | ||
|
8095267d16 | ||
|
444944d57b | ||
|
271cc3fab2 | ||
|
9ed9b132e8 | ||
|
49bca9e60d | ||
|
890b9dc32a | ||
|
719cd89f70 | ||
|
4f97f68161 | ||
|
e143a8cc24 | ||
|
014ad34eea | ||
|
fc7039b169 | ||
|
cdfa4a68c3 | ||
|
3bbd382549 | ||
|
c3d36833d7 | ||
|
a2c1d4f704 | ||
|
b643ac5b31 | ||
|
823830b36c | ||
|
85ac1b29c1 | ||
|
cb8f1fb5c0 | ||
|
554491872c | ||
|
9953bced1b | ||
|
adec20f92c | ||
|
086c6017e0 | ||
|
738f1bf63a | ||
|
783d917ccc | ||
|
cfddbc4758 | ||
|
5160332968 | ||
|
363fe3cba5 | ||
|
2e542d846c | ||
|
942f04defd | ||
|
74e821b440 | ||
|
26f22bab45 | ||
|
76bb2e2818 | ||
|
aa73a77404 | ||
|
5903bbbd53 | ||
|
8358d71bc6 | ||
|
bb05a69934 | ||
|
3cdf7e4865 | ||
|
3b4ab59da4 | ||
|
f495b0b755 | ||
|
470f32c0de | ||
|
962aecdbd5 | ||
|
6b243dea56 | ||
|
d60b6b58ad | ||
|
5c006a0683 | ||
|
ca8ad16294 | ||
|
0e7243fb78 | ||
|
d33369f5bc | ||
|
c158b4f3b7 | ||
|
bcc5fc6e39 | ||
|
d2d3440e9d | ||
|
d9bb79bc0e | ||
|
eaecd44bbe | ||
|
07fa6d10c7 | ||
|
242bf55ff1 | ||
|
45027fb32f | ||
|
61960be5dc | ||
|
665c2b80db | ||
|
1a0be46eba | ||
|
b369cddadf | ||
|
1dc1b89c74 | ||
|
c033481f9b | ||
|
611a1146c5 | ||
|
2d4c8014a4 | ||
|
2aec134810 | ||
|
deae7db4c8 |
136 changed files with 8185 additions and 2449 deletions
66
README.md
66
README.md
|
@ -1,7 +1,7 @@
|
|||
# NEX Protocols Common Go
|
||||
## NEX protocols used by many games with premade handlers and a high level API
|
||||
|
||||
[](https://godoc.org/github.com/PretendoNetwork/nex-protocols-common-go)
|
||||
[](https://godoc.org/github.com/PretendoNetwork/nex-protocols-common-go/v2)
|
||||
|
||||
### Other NEX libraries
|
||||
[nex-go](https://github.com/PretendoNetwork/nex-go) - Barebones NEX/PRUDP server implementation
|
||||
|
@ -10,14 +10,14 @@
|
|||
|
||||
### Install
|
||||
|
||||
`go get github.com/PretendoNetwork/nex-protocols-common-go`
|
||||
`go get github.com/PretendoNetwork/nex-protocols-common-go/v2`
|
||||
|
||||
### Usage
|
||||
|
||||
`nex-protocols-common-go` provides a higher level API than the [NEX Protocols Go module](https://github.com/PretendoNetwork/nex-protocols-go). This module handles many of the more common protcols and methods used shared by many servers. Instead of working directly with the NEX server, this module exposes an API for defining helper functions to provide the module with the data it needs to run
|
||||
|
||||
### Example, friends (Wii U) authentication server
|
||||
### For a complete example, see the complete [Friends Authentication Server](https://github.com/PretendoNetwork/friends-authentication), and other game servers
|
||||
### For a complete example, see the complete [Friends Server](https://github.com/PretendoNetwork/friends), and other game servers
|
||||
|
||||
```go
|
||||
package main
|
||||
|
@ -26,44 +26,52 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/authentication"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
ticket_granting "github.com/PretendoNetwork/nex-protocols-go/v2/ticket-granting"
|
||||
common_ticket_granting "github.com/PretendoNetwork/nex-protocols-common-go/v2/ticket-granting"
|
||||
)
|
||||
|
||||
var nexServer *nex.Server
|
||||
var nexServer *nex.PRUDPServer
|
||||
|
||||
func main() {
|
||||
nexServer = nex.NewServer()
|
||||
nexServer.SetPRUDPVersion(0)
|
||||
nexServer.SetKerberosKeySize(16)
|
||||
nexServer.SetKerberosPassword(os.Getenv("KERBEROS_PASSWORD"))
|
||||
nexServer.SetAccessKey("ridfebb9")
|
||||
nexServer := nex.NewPRUDPServer()
|
||||
|
||||
nexServer.On("Data", func(packet *nex.PacketV0) {
|
||||
request := packet.RMCRequest()
|
||||
endpoint := nex.NewPRUDPEndPoint(1)
|
||||
endpoint.ServerAccount = nex.NewAccount(types.NewPID(1), "Quazal Authentication", "password"))
|
||||
endpoint.AccountDetailsByPID = accountDetailsByPID
|
||||
endpoint.AccountDetailsByUsername = accountDetailsByUsername
|
||||
|
||||
endpoint.OnData(func(packet nex.PacketInterface) {
|
||||
request := packet.RMCMessage()
|
||||
|
||||
fmt.Println("==Friends - Auth==")
|
||||
fmt.Printf("Protocol ID: %#v\n", request.ProtocolID())
|
||||
fmt.Printf("Method ID: %#v\n", request.MethodID())
|
||||
fmt.Printf("Protocol ID: %#v\n", request.ProtocolID)
|
||||
fmt.Printf("Method ID: %#v\n", request.MethodID)
|
||||
fmt.Println("==================")
|
||||
})
|
||||
|
||||
authenticationProtocol := authentication.NewCommonAuthenticationProtocol(nexServer)
|
||||
nexServer.SetFragmentSize(962)
|
||||
nexServer.LibraryVersions.SetDefault(nex.NewLibraryVersion(1, 1, 0))
|
||||
nexServer.SessionKeyLength = 16
|
||||
nexServer.AccessKey = "ridfebb9"
|
||||
|
||||
ticketGrantingProtocol := ticket_granting.NewProtocol(endpoint)
|
||||
endpoint.RegisterServiceProtocol(ticketGrantingProtocol)
|
||||
commonTicketGrantingProtocol := common_ticket_granting.NewCommonProtocol(ticketGrantingProtocol)
|
||||
|
||||
secureStationURL := nex.NewStationURL("")
|
||||
secureStationURL.SetScheme("prudps")
|
||||
secureStationURL.SetAddress(os.Getenv("SECURE_SERVER_LOCATION"))
|
||||
secureStationURL.SetPort(os.Getenv("SECURE_SERVER_PORT"))
|
||||
secureStationURL.SetCID("1")
|
||||
secureStationURL.SetPID("2")
|
||||
secureStationURL.SetSID("1")
|
||||
secureStationURL.SetStream("10")
|
||||
secureStationURL.SetType("2")
|
||||
secureStationURL.Scheme = "prudps"
|
||||
secureStationURL.Fields.Set("address", os.Getenv("SECURE_SERVER_LOCATION"))
|
||||
secureStationURL.Fields.Set("port", os.Getenv("SECURE_SERVER_PORT"))
|
||||
secureStationURL.Fields.Set("CID", "1")
|
||||
secureStationURL.Fields.Set("PID", "2")
|
||||
secureStationURL.Fields.Set("sid", "1")
|
||||
secureStationURL.Fields.Set("stream", "10")
|
||||
secureStationURL.Fields.Set("type", "2")
|
||||
|
||||
authenticationProtocol.SetSecureStationURL(secureStationURL)
|
||||
authenticationProtocol.SetBuildName("Pretendo Friends Auth")
|
||||
authenticationProtocol.SetPasswordFromPIDFunction(passwordFromPID)
|
||||
commonTicketGrantingProtocol.SecureStationURL = secureStationURL
|
||||
commonTicketGrantingProtocol.BuildName = "Pretendo Friends Auth"
|
||||
|
||||
nexServer.Listen(":60000")
|
||||
nexServer.Listen(60000)
|
||||
}
|
||||
```
|
||||
```
|
||||
|
|
81
datastore/change_meta.go
Normal file
81
datastore/change_meta.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) changeMeta(err error, packet nex.PacketInterface, callID uint32, param datastore_types.DataStoreChangeMetaParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfoByDataID == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.UpdateObjectPeriodByDataIDWithPassword == nil {
|
||||
common_globals.Logger.Warning("UpdateObjectPeriodByDataIDWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.UpdateObjectMetaBinaryByDataIDWithPassword == nil {
|
||||
common_globals.Logger.Warning("UpdateObjectMetaBinaryByDataIDWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.UpdateObjectDataTypeByDataIDWithPassword == nil {
|
||||
common_globals.Logger.Warning("UpdateObjectDataTypeByDataIDWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
metaInfo, errCode := commonProtocol.GetObjectInfoByDataID(param.DataID)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
// TODO - Is this the right permission?
|
||||
errCode = commonProtocol.VerifyObjectPermission(metaInfo.OwnerID, connection.PID(), metaInfo.DelPermission)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
if uint32(param.ModifiesFlag) & 0x08 != 0 {
|
||||
errCode = commonProtocol.UpdateObjectPeriodByDataIDWithPassword(param.DataID, param.Period, param.UpdatePassword)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
}
|
||||
|
||||
if uint32(param.ModifiesFlag) & 0x10 != 0 {
|
||||
errCode = commonProtocol.UpdateObjectMetaBinaryByDataIDWithPassword(param.DataID, param.MetaBinary, param.UpdatePassword)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
}
|
||||
|
||||
if uint32(param.ModifiesFlag) & 0x80 != 0 {
|
||||
errCode = commonProtocol.UpdateObjectDataTypeByDataIDWithPassword(param.DataID, param.DataType, param.UpdatePassword)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
}
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodChangeMeta
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterChangeMeta != nil {
|
||||
go commonProtocol.OnAfterChangeMeta(packet, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
111
datastore/complete_post_object.go
Normal file
111
datastore/complete_post_object.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) completePostObject(err error, packet nex.PacketInterface, callID uint32, param datastore_types.DataStoreCompletePostParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.minIOClient == nil {
|
||||
common_globals.Logger.Warning("MinIOClient not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.GetObjectInfoByDataID == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.GetObjectOwnerByDataID == nil {
|
||||
common_globals.Logger.Warning("GetObjectOwnerByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.GetObjectSizeByDataID == nil {
|
||||
common_globals.Logger.Warning("GetObjectSizeByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.UpdateObjectUploadCompletedByDataID == nil {
|
||||
common_globals.Logger.Warning("UpdateObjectUploadCompletedByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.DeleteObjectByDataID == nil {
|
||||
common_globals.Logger.Warning("DeleteObjectByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
// * If GetObjectInfoByDataID returns data then that means
|
||||
// * the object has already been marked as uploaded. So do
|
||||
// * nothing
|
||||
_, errCode := commonProtocol.GetObjectInfoByDataID(param.DataID)
|
||||
if errCode == nil {
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
// * Only allow an objects owner to make this request
|
||||
ownerPID, errCode := commonProtocol.GetObjectOwnerByDataID(param.DataID)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
if ownerPID != uint32(connection.PID()) {
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
bucket := commonProtocol.S3Bucket
|
||||
key := fmt.Sprintf("%s/%d.bin", commonProtocol.s3DataKeyBase, param.DataID)
|
||||
|
||||
if param.IsSuccess {
|
||||
objectSizeS3, err := commonProtocol.S3ObjectSize(bucket, key)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.NotFound, "change_error")
|
||||
}
|
||||
|
||||
objectSizeDB, errCode := commonProtocol.GetObjectSizeByDataID(param.DataID)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
if objectSizeS3 != uint64(objectSizeDB) {
|
||||
common_globals.Logger.Errorf("Object with DataID %d did not upload correctly! Mismatched sizes", param.DataID)
|
||||
// TODO - Is this a good error?
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
errCode = commonProtocol.UpdateObjectUploadCompletedByDataID(param.DataID, true)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
} else {
|
||||
errCode := commonProtocol.DeleteObjectByDataID(param.DataID)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
}
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodCompletePostObject
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterCompletePostObject != nil {
|
||||
go commonProtocol.OnAfterCompletePostObject(packet, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
83
datastore/complete_post_objects.go
Normal file
83
datastore/complete_post_objects.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) completePostObjects(err error, packet nex.PacketInterface, callID uint32, dataIDs types.List[types.UInt64]) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.minIOClient == nil {
|
||||
common_globals.Logger.Warning("MinIOClient not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.GetObjectSizeByDataID == nil {
|
||||
common_globals.Logger.Warning("GetObjectSizeByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.UpdateObjectUploadCompletedByDataID == nil {
|
||||
common_globals.Logger.Warning("UpdateObjectUploadCompletedByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
var errorCode *nex.Error
|
||||
|
||||
for _, dataID := range dataIDs {
|
||||
bucket := commonProtocol.S3Bucket
|
||||
key := fmt.Sprintf("%s/%d.bin", commonProtocol.s3DataKeyBase, dataID)
|
||||
|
||||
objectSizeS3, err := commonProtocol.S3ObjectSize(bucket, key)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
errorCode = nex.NewError(nex.ResultCodes.DataStore.NotFound, "change_error")
|
||||
break
|
||||
}
|
||||
|
||||
objectSizeDB, errCode := commonProtocol.GetObjectSizeByDataID(dataID)
|
||||
if errCode != nil {
|
||||
errorCode = errCode
|
||||
break
|
||||
}
|
||||
|
||||
if objectSizeS3 != uint64(objectSizeDB) {
|
||||
common_globals.Logger.Errorf("Object with DataID %d did not upload correctly! Mismatched sizes", dataID)
|
||||
// TODO - Is this a good error?
|
||||
errorCode = nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
break
|
||||
}
|
||||
|
||||
errCode = commonProtocol.UpdateObjectUploadCompletedByDataID(dataID, true)
|
||||
if errCode != nil {
|
||||
errorCode = errCode
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if errorCode != nil {
|
||||
return nil, errorCode
|
||||
}
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodCompletePostObjects
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterCompletePostObjects != nil {
|
||||
go commonProtocol.OnAfterCompletePostObjects(packet, dataIDs)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
54
datastore/delete_object.go
Normal file
54
datastore/delete_object.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) deleteObject(err error, packet nex.PacketInterface, callID uint32, param datastore_types.DataStoreDeleteParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfoByDataID == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.DeleteObjectByDataIDWithPassword == nil {
|
||||
common_globals.Logger.Warning("DeleteObjectByDataIDWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
metaInfo, errCode := commonProtocol.GetObjectInfoByDataID(param.DataID)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
errCode = commonProtocol.VerifyObjectPermission(metaInfo.OwnerID, connection.PID(), metaInfo.DelPermission)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
errCode = commonProtocol.DeleteObjectByDataIDWithPassword(param.DataID, param.UpdatePassword)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodDeleteObject
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterDeleteObject != nil {
|
||||
go commonProtocol.OnAfterDeleteObject(packet, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
66
datastore/get_meta.go
Normal file
66
datastore/get_meta.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) getMeta(err error, packet nex.PacketInterface, callID uint32, param datastore_types.DataStoreGetMetaParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfoByPersistenceTargetWithPassword == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByPersistenceTargetWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.GetObjectInfoByDataIDWithPassword == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataIDWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
var pMetaInfo datastore_types.DataStoreMetaInfo
|
||||
var errCode *nex.Error
|
||||
|
||||
// * Real server ignores PersistenceTarget if DataID is set
|
||||
if param.DataID == 0 {
|
||||
pMetaInfo, errCode = commonProtocol.GetObjectInfoByPersistenceTargetWithPassword(param.PersistenceTarget, param.AccessPassword)
|
||||
} else {
|
||||
pMetaInfo, errCode = commonProtocol.GetObjectInfoByDataIDWithPassword(param.DataID, param.AccessPassword)
|
||||
}
|
||||
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
errCode = commonProtocol.VerifyObjectPermission(pMetaInfo.OwnerID, connection.PID(), pMetaInfo.Permission)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
pMetaInfo.FilterPropertiesByResultOption(param.ResultOption)
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
pMetaInfo.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodGetMeta
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterGetMeta != nil {
|
||||
go commonProtocol.OnAfterGetMeta(packet, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
75
datastore/get_metas.go
Normal file
75
datastore/get_metas.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) getMetas(err error, packet nex.PacketInterface, callID uint32, dataIDs types.List[types.UInt64], param datastore_types.DataStoreGetMetaParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfoByDataID == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
// TODO - Verify if param.PersistenceTarget is respected? It wouldn't make sense here but who knows
|
||||
|
||||
pMetaInfo := types.NewList[datastore_types.DataStoreMetaInfo]()
|
||||
pResults := types.NewList[types.QResult]()
|
||||
|
||||
// * param has an AccessPassword, but it goes unchecked here.
|
||||
// * The password would need to be the same for every object
|
||||
// * in the input array, which doesn't make any sense. Assuming
|
||||
// * it's unused until proven otherwise
|
||||
|
||||
for _, dataID := range dataIDs {
|
||||
objectInfo, errCode := commonProtocol.GetObjectInfoByDataID(dataID)
|
||||
|
||||
if errCode != nil {
|
||||
objectInfo = datastore_types.NewDataStoreMetaInfo()
|
||||
|
||||
pResults = append(pResults, types.NewQResultError(errCode.ResultCode))
|
||||
} else {
|
||||
errCode = commonProtocol.VerifyObjectPermission(objectInfo.OwnerID, connection.PID(), objectInfo.Permission)
|
||||
if errCode != nil {
|
||||
objectInfo = datastore_types.NewDataStoreMetaInfo()
|
||||
|
||||
pResults = append(pResults, types.NewQResultError(errCode.ResultCode))
|
||||
} else {
|
||||
pResults = append(pResults, types.NewQResultSuccess(nex.ResultCodes.DataStore.Unknown))
|
||||
}
|
||||
|
||||
objectInfo.FilterPropertiesByResultOption(param.ResultOption)
|
||||
}
|
||||
|
||||
pMetaInfo = append(pMetaInfo, objectInfo)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
pMetaInfo.WriteTo(rmcResponseStream)
|
||||
pResults.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodGetMetas
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterGetMetas != nil {
|
||||
go commonProtocol.OnAfterGetMetas(packet, dataIDs, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
81
datastore/get_metas_multiple_param.go
Normal file
81
datastore/get_metas_multiple_param.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) getMetasMultipleParam(err error, packet nex.PacketInterface, callID uint32, params types.List[datastore_types.DataStoreGetMetaParam]) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfoByPersistenceTargetWithPassword == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByPersistenceTargetWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.GetObjectInfoByDataIDWithPassword == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataIDWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
pMetaInfo := types.NewList[datastore_types.DataStoreMetaInfo]()
|
||||
pResults := types.NewList[types.QResult]()
|
||||
|
||||
for _, param := range params {
|
||||
var objectInfo datastore_types.DataStoreMetaInfo
|
||||
var errCode *nex.Error
|
||||
|
||||
// * Real server ignores PersistenceTarget if DataID is set
|
||||
if param.DataID == 0 {
|
||||
objectInfo, errCode = commonProtocol.GetObjectInfoByPersistenceTargetWithPassword(param.PersistenceTarget, param.AccessPassword)
|
||||
} else {
|
||||
objectInfo, errCode = commonProtocol.GetObjectInfoByDataIDWithPassword(param.DataID, param.AccessPassword)
|
||||
}
|
||||
|
||||
if errCode != nil {
|
||||
objectInfo = datastore_types.NewDataStoreMetaInfo()
|
||||
|
||||
pResults = append(pResults, types.NewQResultError(errCode.ResultCode))
|
||||
} else {
|
||||
errCode = commonProtocol.VerifyObjectPermission(objectInfo.OwnerID, connection.PID(), objectInfo.Permission)
|
||||
if errCode != nil {
|
||||
objectInfo = datastore_types.NewDataStoreMetaInfo()
|
||||
|
||||
pResults = append(pResults, types.NewQResultError(errCode.ResultCode))
|
||||
} else {
|
||||
pResults = append(pResults, types.NewQResultSuccess(nex.ResultCodes.DataStore.Unknown))
|
||||
}
|
||||
|
||||
objectInfo.FilterPropertiesByResultOption(param.ResultOption)
|
||||
}
|
||||
|
||||
pMetaInfo = append(pMetaInfo, objectInfo)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
pMetaInfo.WriteTo(rmcResponseStream)
|
||||
pResults.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodGetMetasMultipleParam
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterGetMetasMultipleParam != nil {
|
||||
go commonProtocol.OnAfterGetMetasMultipleParam(packet, params)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
72
datastore/post_meta_binary.go
Normal file
72
datastore/post_meta_binary.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) postMetaBinary(err error, packet nex.PacketInterface, callID uint32, param datastore_types.DataStorePreparePostParam) (*nex.RMCMessage, *nex.Error) {
|
||||
// * This method looks to function identically to DataStore::PreparePostObject,
|
||||
// * except the only difference being it doesn't return an S3 upload URL. This
|
||||
// * needs to be verified though, as there are other methods in the family such
|
||||
// * as DataStore::PostMetaBinaryWithDataID which make less sense in this context,
|
||||
// * unless those are just used to *update* a meta binary? Or maybe the DataID in
|
||||
// * those methods is a pre-allocated DataID from the server? Needs more testing
|
||||
|
||||
if commonProtocol.InitializeObjectByPreparePostParam == nil {
|
||||
common_globals.Logger.Warning("InitializeObjectByPreparePostParam not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.InitializeObjectRatingWithSlot == nil {
|
||||
common_globals.Logger.Warning("InitializeObjectRatingWithSlot not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
// TODO - Need to verify what param.PersistenceInitParam.DeleteLastObject really means. It's often set to true even when it wouldn't make sense
|
||||
dataID, errCode := commonProtocol.InitializeObjectByPreparePostParam(connection.PID(), param)
|
||||
if errCode != nil {
|
||||
common_globals.Logger.Errorf("Error code on object init: %s", errCode.Error())
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
// TODO - Should this be moved to InitializeObjectByPreparePostParam?
|
||||
for _ , ratingInitParamWithSlot := range param.RatingInitParams {
|
||||
errCode = commonProtocol.InitializeObjectRatingWithSlot(dataID, ratingInitParamWithSlot)
|
||||
if errCode != nil {
|
||||
common_globals.Logger.Errorf("Error code on rating init: %s", errCode.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
rmcResponseStream.WriteUInt64LE(dataID)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodPostMetaBinary
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterPostMetaBinary != nil {
|
||||
go commonProtocol.OnAfterPostMetaBinary(packet, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
90
datastore/prepare_get_object.go
Normal file
90
datastore/prepare_get_object.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) prepareGetObject(err error, packet nex.PacketInterface, callID uint32, param datastore_types.DataStorePrepareGetParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfoByDataID == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataID not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.S3Presigner == nil {
|
||||
common_globals.Logger.Warning("S3Presigner not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
var objectInfo datastore_types.DataStoreMetaInfo
|
||||
var errCode *nex.Error
|
||||
|
||||
// * Real server ignores PersistenceTarget if DataID is set
|
||||
if param.DataID == 0 {
|
||||
objectInfo, errCode = commonProtocol.GetObjectInfoByPersistenceTargetWithPassword(param.PersistenceTarget, param.AccessPassword)
|
||||
} else {
|
||||
objectInfo, errCode = commonProtocol.GetObjectInfoByDataIDWithPassword(param.DataID, param.AccessPassword)
|
||||
}
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
errCode = commonProtocol.VerifyObjectPermission(objectInfo.OwnerID, connection.PID(), objectInfo.Permission)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
bucket := commonProtocol.S3Bucket
|
||||
key := fmt.Sprintf("%s/%d.bin", commonProtocol.s3DataKeyBase, objectInfo.DataID)
|
||||
|
||||
url, err := commonProtocol.S3Presigner.GetObject(bucket, key, time.Minute*15)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.OperationNotAllowed, "change_error")
|
||||
}
|
||||
|
||||
requestHeaders, errCode := commonProtocol.S3GetRequestHeaders()
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
pReqGetInfo := datastore_types.NewDataStoreReqGetInfo()
|
||||
|
||||
pReqGetInfo.URL = types.NewString(url.String())
|
||||
pReqGetInfo.RequestHeaders = types.NewList[datastore_types.DataStoreKeyValue]()
|
||||
pReqGetInfo.Size = objectInfo.Size.Copy().(types.UInt32)
|
||||
pReqGetInfo.RootCACert = types.NewBuffer(commonProtocol.RootCACert)
|
||||
pReqGetInfo.DataID = param.DataID
|
||||
pReqGetInfo.RequestHeaders = requestHeaders
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
pReqGetInfo.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodPrepareGetObject
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterPrepareGetObject != nil {
|
||||
go commonProtocol.OnAfterPrepareGetObject(packet, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
105
datastore/prepare_post_object.go
Normal file
105
datastore/prepare_post_object.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) preparePostObject(err error, packet nex.PacketInterface, callID uint32, param datastore_types.DataStorePreparePostParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.InitializeObjectByPreparePostParam == nil {
|
||||
common_globals.Logger.Warning("InitializeObjectByPreparePostParam not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.InitializeObjectRatingWithSlot == nil {
|
||||
common_globals.Logger.Warning("InitializeObjectRatingWithSlot not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.S3Presigner == nil {
|
||||
common_globals.Logger.Warning("S3Presigner not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
// TODO - Need to verify what param.PersistenceInitParam.DeleteLastObject really means. It's often set to true even when it wouldn't make sense
|
||||
dataID, errCode := commonProtocol.InitializeObjectByPreparePostParam(connection.PID(), param)
|
||||
if errCode != nil {
|
||||
common_globals.Logger.Errorf("Error on object init: %s", errCode.Error())
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
// TODO - Should this be moved to InitializeObjectByPreparePostParam?
|
||||
for _ , ratingInitParamWithSlot := range param.RatingInitParams {
|
||||
errCode = commonProtocol.InitializeObjectRatingWithSlot(dataID, ratingInitParamWithSlot)
|
||||
if errCode != nil {
|
||||
common_globals.Logger.Errorf("Error on rating init: %s", errCode.Error())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
bucket := commonProtocol.S3Bucket
|
||||
key := fmt.Sprintf("%s/%d.bin", commonProtocol.s3DataKeyBase, dataID)
|
||||
|
||||
URL, formData, err := commonProtocol.S3Presigner.PostObject(bucket, key, time.Minute*15)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.OperationNotAllowed, "change_error")
|
||||
}
|
||||
|
||||
requestHeaders, errCode := commonProtocol.S3PostRequestHeaders()
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
pReqPostInfo := datastore_types.NewDataStoreReqPostInfo()
|
||||
|
||||
pReqPostInfo.DataID = types.NewUInt64(dataID)
|
||||
pReqPostInfo.URL = types.NewString(URL.String())
|
||||
pReqPostInfo.RequestHeaders = types.NewList[datastore_types.DataStoreKeyValue]()
|
||||
pReqPostInfo.FormFields = types.NewList[datastore_types.DataStoreKeyValue]()
|
||||
pReqPostInfo.RootCACert = types.NewBuffer(commonProtocol.RootCACert)
|
||||
pReqPostInfo.RequestHeaders = requestHeaders
|
||||
|
||||
for key, value := range formData {
|
||||
field := datastore_types.NewDataStoreKeyValue()
|
||||
field.Key = types.NewString(key)
|
||||
field.Value = types.NewString(value)
|
||||
|
||||
pReqPostInfo.FormFields = append(pReqPostInfo.FormFields, field)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
pReqPostInfo.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodPreparePostObject
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterPreparePostObject != nil {
|
||||
go commonProtocol.OnAfterPreparePostObject(packet, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
163
datastore/protocol.go
Normal file
163
datastore/protocol.go
Normal file
|
@ -0,0 +1,163 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
"github.com/minio/minio-go/v7"
|
||||
)
|
||||
|
||||
type CommonProtocol struct {
|
||||
endpoint nex.EndpointInterface
|
||||
protocol datastore.Interface
|
||||
S3Bucket string
|
||||
s3DataKeyBase string
|
||||
s3NotifyKeyBase string
|
||||
RootCACert []byte
|
||||
minIOClient *minio.Client
|
||||
S3Presigner S3PresignerInterface
|
||||
GetUserFriendPIDs func(pid uint32) []uint32
|
||||
GetObjectInfoByDataID func(dataID types.UInt64) (datastore_types.DataStoreMetaInfo, *nex.Error)
|
||||
UpdateObjectPeriodByDataIDWithPassword func(dataID types.UInt64, dataType types.UInt16, password types.UInt64) *nex.Error
|
||||
UpdateObjectMetaBinaryByDataIDWithPassword func(dataID types.UInt64, metaBinary types.QBuffer, password types.UInt64) *nex.Error
|
||||
UpdateObjectDataTypeByDataIDWithPassword func(dataID types.UInt64, period types.UInt16, password types.UInt64) *nex.Error
|
||||
GetObjectSizeByDataID func(dataID types.UInt64) (uint32, *nex.Error)
|
||||
UpdateObjectUploadCompletedByDataID func(dataID types.UInt64, uploadCompleted bool) *nex.Error
|
||||
GetObjectInfoByPersistenceTargetWithPassword func(persistenceTarget datastore_types.DataStorePersistenceTarget, password types.UInt64) (datastore_types.DataStoreMetaInfo, *nex.Error)
|
||||
GetObjectInfoByDataIDWithPassword func(dataID types.UInt64, password types.UInt64) (datastore_types.DataStoreMetaInfo, *nex.Error)
|
||||
S3GetRequestHeaders func() ([]datastore_types.DataStoreKeyValue, *nex.Error)
|
||||
S3PostRequestHeaders func() ([]datastore_types.DataStoreKeyValue, *nex.Error)
|
||||
InitializeObjectByPreparePostParam func(ownerPID types.PID, param datastore_types.DataStorePreparePostParam) (uint64, *nex.Error)
|
||||
InitializeObjectRatingWithSlot func(dataID uint64, param datastore_types.DataStoreRatingInitParamWithSlot) *nex.Error
|
||||
RateObjectWithPassword func(dataID types.UInt64, slot types.UInt8, ratingValue types.Int32, accessPassword types.UInt64) (datastore_types.DataStoreRatingInfo, *nex.Error)
|
||||
DeleteObjectByDataIDWithPassword func(dataID types.UInt64, password types.UInt64) *nex.Error
|
||||
DeleteObjectByDataID func(dataID types.UInt64) *nex.Error
|
||||
GetObjectInfosByDataStoreSearchParam func(param datastore_types.DataStoreSearchParam, pid types.PID) ([]datastore_types.DataStoreMetaInfo, uint32, *nex.Error)
|
||||
GetObjectOwnerByDataID func(dataID types.UInt64) (uint32, *nex.Error)
|
||||
OnAfterDeleteObject func(packet nex.PacketInterface, param datastore_types.DataStoreDeleteParam)
|
||||
OnAfterGetMeta func(packet nex.PacketInterface, param datastore_types.DataStoreGetMetaParam)
|
||||
OnAfterGetMetas func(packet nex.PacketInterface, dataIDs types.List[types.UInt64], param datastore_types.DataStoreGetMetaParam)
|
||||
OnAfterSearchObject func(packet nex.PacketInterface, param datastore_types.DataStoreSearchParam)
|
||||
OnAfterRateObject func(packet nex.PacketInterface, target datastore_types.DataStoreRatingTarget, param datastore_types.DataStoreRateObjectParam, fetchRatings types.Bool)
|
||||
OnAfterPostMetaBinary func(packet nex.PacketInterface, param datastore_types.DataStorePreparePostParam)
|
||||
OnAfterPreparePostObject func(packet nex.PacketInterface, param datastore_types.DataStorePreparePostParam)
|
||||
OnAfterPrepareGetObject func(packet nex.PacketInterface, param datastore_types.DataStorePrepareGetParam)
|
||||
OnAfterCompletePostObject func(packet nex.PacketInterface, param datastore_types.DataStoreCompletePostParam)
|
||||
OnAfterGetMetasMultipleParam func(packet nex.PacketInterface, params types.List[datastore_types.DataStoreGetMetaParam])
|
||||
OnAfterCompletePostObjects func(packet nex.PacketInterface, dataIDs types.List[types.UInt64])
|
||||
OnAfterChangeMeta func(packet nex.PacketInterface, param datastore_types.DataStoreChangeMetaParam)
|
||||
OnAfterRateObjects func(packet nex.PacketInterface, targets types.List[datastore_types.DataStoreRatingTarget], params types.List[datastore_types.DataStoreRateObjectParam], transactional types.Bool, fetchRatings types.Bool)
|
||||
}
|
||||
|
||||
func (c *CommonProtocol) S3StatObject(bucket, key string) (minio.ObjectInfo, error) {
|
||||
return c.minIOClient.StatObject(context.TODO(), bucket, key, minio.StatObjectOptions{})
|
||||
}
|
||||
|
||||
func (c *CommonProtocol) S3ObjectSize(bucket, key string) (uint64, error) {
|
||||
info, err := c.S3StatObject(bucket, key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint64(info.Size), nil
|
||||
}
|
||||
|
||||
func (c *CommonProtocol) VerifyObjectPermission(ownerPID, accessorPID types.PID, permission datastore_types.DataStorePermission) *nex.Error {
|
||||
if permission.Permission > 3 {
|
||||
return nex.NewError(nex.ResultCodes.DataStore.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
// * Owner can always access their own objects
|
||||
if ownerPID.Equals(accessorPID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// * Allow anyone
|
||||
if permission.Permission == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// * Allow only friends of the owner
|
||||
if permission.Permission == 1 {
|
||||
// TODO - This assumes a legacy client. Will not work on the Switch
|
||||
friendsList := c.GetUserFriendPIDs(uint32(ownerPID))
|
||||
|
||||
if !slices.Contains(friendsList, uint32(accessorPID)) {
|
||||
return nex.NewError(nex.ResultCodes.DataStore.PermissionDenied, "change_error")
|
||||
}
|
||||
}
|
||||
|
||||
// * Allow only users whose PIDs are defined in permission.RecipientIDs
|
||||
if permission.Permission == 2 {
|
||||
if !permission.RecipientIDs.Contains(accessorPID) {
|
||||
return nex.NewError(nex.ResultCodes.DataStore.PermissionDenied, "change_error")
|
||||
}
|
||||
}
|
||||
|
||||
// * Allow only the owner
|
||||
if permission.Permission == 3 {
|
||||
if !ownerPID.Equals(accessorPID) {
|
||||
return nex.NewError(nex.ResultCodes.DataStore.PermissionDenied, "change_error")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDataKeyBase sets the base for the key to be used when uploading standard DataStore objects
|
||||
func (c *CommonProtocol) SetDataKeyBase(base string) {
|
||||
// * Just in case someone passes a badly formatted key
|
||||
base = strings.TrimPrefix(base, "/")
|
||||
base = strings.TrimSuffix(base, "/")
|
||||
c.s3DataKeyBase = base
|
||||
}
|
||||
|
||||
// SetNotifyKeyBase sets the base for the key to be used when uploading DataStore notification data
|
||||
func (c *CommonProtocol) SetNotifyKeyBase(base string) {
|
||||
// * Just in case someone passes a badly formatted key
|
||||
base = strings.TrimPrefix(base, "/")
|
||||
base = strings.TrimSuffix(base, "/")
|
||||
c.s3NotifyKeyBase = base
|
||||
}
|
||||
|
||||
// SetMinIOClient sets the MinIO S3 client
|
||||
func (c *CommonProtocol) SetMinIOClient(client *minio.Client) {
|
||||
c.minIOClient = client
|
||||
c.S3Presigner = NewS3Presigner(c.minIOClient)
|
||||
}
|
||||
|
||||
// NewCommonProtocol returns a new CommonProtocol
|
||||
func NewCommonProtocol(protocol datastore.Interface) *CommonProtocol {
|
||||
commonProtocol := &CommonProtocol{
|
||||
endpoint: protocol.Endpoint(),
|
||||
protocol: protocol,
|
||||
RootCACert: []byte{},
|
||||
S3GetRequestHeaders: func() ([]datastore_types.DataStoreKeyValue, *nex.Error) {
|
||||
return []datastore_types.DataStoreKeyValue{}, nil
|
||||
},
|
||||
S3PostRequestHeaders: func() ([]datastore_types.DataStoreKeyValue, *nex.Error) {
|
||||
return []datastore_types.DataStoreKeyValue{}, nil
|
||||
},
|
||||
}
|
||||
|
||||
protocol.SetHandlerDeleteObject(commonProtocol.deleteObject)
|
||||
protocol.SetHandlerGetMeta(commonProtocol.getMeta)
|
||||
protocol.SetHandlerGetMetas(commonProtocol.getMetas)
|
||||
protocol.SetHandlerSearchObject(commonProtocol.searchObject)
|
||||
protocol.SetHandlerRateObject(commonProtocol.rateObject)
|
||||
protocol.SetHandlerPostMetaBinary(commonProtocol.postMetaBinary)
|
||||
protocol.SetHandlerPreparePostObject(commonProtocol.preparePostObject)
|
||||
protocol.SetHandlerPrepareGetObject(commonProtocol.prepareGetObject)
|
||||
protocol.SetHandlerCompletePostObject(commonProtocol.completePostObject)
|
||||
protocol.SetHandlerGetMetasMultipleParam(commonProtocol.getMetasMultipleParam)
|
||||
protocol.SetHandlerCompletePostObjects(commonProtocol.completePostObjects)
|
||||
protocol.SetHandlerChangeMeta(commonProtocol.changeMeta)
|
||||
protocol.SetHandlerRateObjects(commonProtocol.rateObjects)
|
||||
|
||||
return commonProtocol
|
||||
}
|
69
datastore/rate_object.go
Normal file
69
datastore/rate_object.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) rateObject(err error, packet nex.PacketInterface, callID uint32, target datastore_types.DataStoreRatingTarget, param datastore_types.DataStoreRateObjectParam, fetchRatings types.Bool) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfoByDataIDWithPassword == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataIDWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.RateObjectWithPassword == nil {
|
||||
common_globals.Logger.Warning("RateObjectWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
objectInfo, errCode := commonProtocol.GetObjectInfoByDataIDWithPassword(target.DataID, param.AccessPassword)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
errCode = commonProtocol.VerifyObjectPermission(objectInfo.OwnerID, connection.PID(), objectInfo.Permission)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
pRating, errCode := commonProtocol.RateObjectWithPassword(target.DataID, target.Slot, param.RatingValue, param.AccessPassword)
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
// * This is kinda backwards. Server returns
|
||||
// * the rating by default, so we check if
|
||||
// * the client DOESN'T want it and then just
|
||||
// * zero it out
|
||||
if !fetchRatings {
|
||||
pRating = datastore_types.NewDataStoreRatingInfo()
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
pRating.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodRateObject
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterRateObject != nil {
|
||||
go commonProtocol.OnAfterRateObject(packet, target, param, fetchRatings)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
91
datastore/rate_objects.go
Normal file
91
datastore/rate_objects.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) rateObjects(err error, packet nex.PacketInterface, callID uint32, targets types.List[datastore_types.DataStoreRatingTarget], params types.List[datastore_types.DataStoreRateObjectParam], transactional types.Bool, fetchRatings types.Bool) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfoByDataIDWithPassword == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfoByDataIDWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if commonProtocol.RateObjectWithPassword == nil {
|
||||
common_globals.Logger.Warning("RateObjectWithPassword not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
pRatings := types.NewList[datastore_types.DataStoreRatingInfo]()
|
||||
pResults := types.NewList[types.QResult]()
|
||||
|
||||
// * Real DataStore does not actually check this.
|
||||
// * I just didn't feel like working out the
|
||||
// * logic for differing sized lists. So force
|
||||
// * them to always be the same
|
||||
if len(targets) != len(params) {
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
var errorCode *nex.Error
|
||||
|
||||
for i, target := range targets {
|
||||
// * We already checked that targets and params will have the same length
|
||||
param := params[i]
|
||||
|
||||
objectInfo, errCode := commonProtocol.GetObjectInfoByDataIDWithPassword(target.DataID, param.AccessPassword)
|
||||
if errCode != nil {
|
||||
errorCode = errCode
|
||||
break
|
||||
}
|
||||
|
||||
errCode = commonProtocol.VerifyObjectPermission(objectInfo.OwnerID, connection.PID(), objectInfo.Permission)
|
||||
if errCode != nil {
|
||||
errorCode = errCode
|
||||
break
|
||||
}
|
||||
|
||||
rating, errCode := commonProtocol.RateObjectWithPassword(target.DataID, target.Slot, param.RatingValue, param.AccessPassword)
|
||||
if errCode != nil {
|
||||
errorCode = errCode
|
||||
break
|
||||
}
|
||||
|
||||
if fetchRatings {
|
||||
pRatings = append(pRatings, rating)
|
||||
}
|
||||
}
|
||||
|
||||
if errorCode != nil {
|
||||
return nil, errorCode
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
pRatings.WriteTo(rmcResponseStream)
|
||||
pResults.WriteTo(rmcResponseStream) // * pResults is ALWAYS empty in SMM?
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodRateObjects
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterRateObjects != nil {
|
||||
go commonProtocol.OnAfterRateObjects(packet, targets, params, transactional, fetchRatings)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
51
datastore/s3_presigner.go
Normal file
51
datastore/s3_presigner.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
)
|
||||
|
||||
type S3PresignerInterface interface {
|
||||
GetObject(bucket, key string, lifetime time.Duration) (*url.URL, error)
|
||||
PostObject(bucket, key string, lifetime time.Duration) (*url.URL, map[string]string, error)
|
||||
}
|
||||
|
||||
type S3Presigner struct {
|
||||
minio *minio.Client
|
||||
}
|
||||
|
||||
func (p *S3Presigner) GetObject(bucket, key string, lifetime time.Duration) (*url.URL, error) {
|
||||
reqParams := make(url.Values)
|
||||
|
||||
return p.minio.PresignedGetObject(context.Background(), bucket, key, lifetime, reqParams)
|
||||
}
|
||||
|
||||
func (p *S3Presigner) PostObject(bucket, key string, lifetime time.Duration) (*url.URL, map[string]string, error) {
|
||||
policy := minio.NewPostPolicy()
|
||||
|
||||
err := policy.SetBucket(bucket)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = policy.SetKey(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = policy.SetExpires(time.Now().UTC().Add(lifetime).UTC())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return p.minio.PresignedPostPolicy(context.Background(), policy)
|
||||
}
|
||||
|
||||
func NewS3Presigner(minioClient *minio.Client) *S3Presigner {
|
||||
return &S3Presigner{
|
||||
minio: minioClient,
|
||||
}
|
||||
}
|
97
datastore/search_object.go
Normal file
97
datastore/search_object.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-go/v2/datastore"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/v2/datastore/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) searchObject(err error, packet nex.PacketInterface, callID uint32, param datastore_types.DataStoreSearchParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.GetObjectInfosByDataStoreSearchParam == nil {
|
||||
common_globals.Logger.Warning("GetObjectInfosByDataStoreSearchParam not defined")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.DataStore.Unknown, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender()
|
||||
endpoint := connection.Endpoint()
|
||||
|
||||
// * This is likely game-specific. Also developer note:
|
||||
// * Please keep in mind that no results is allowed. errCode
|
||||
// * should NEVER be DataStore::NotFound!
|
||||
// *
|
||||
// * DataStoreSearchParam contains a ResultRange to limit the
|
||||
// * returned results. TotalCount is the total matching objects
|
||||
// * in the database, whereas objects is the limited results
|
||||
objects, totalCount, errCode := commonProtocol.GetObjectInfosByDataStoreSearchParam(param, connection.PID())
|
||||
if errCode != nil {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
pSearchResult := datastore_types.NewDataStoreSearchResult()
|
||||
|
||||
pSearchResult.Result = types.NewList[datastore_types.DataStoreMetaInfo]()
|
||||
|
||||
for _, object := range objects {
|
||||
errCode = commonProtocol.VerifyObjectPermission(object.OwnerID, connection.PID(), object.Permission)
|
||||
if errCode != nil {
|
||||
// * Since we don't error here, should we also
|
||||
// * "hide" these results by also decrementing
|
||||
// * totalCount?
|
||||
continue
|
||||
}
|
||||
|
||||
object.FilterPropertiesByResultOption(param.ResultOption)
|
||||
|
||||
pSearchResult.Result = append(pSearchResult.Result, object)
|
||||
}
|
||||
|
||||
var totalCountType uint8
|
||||
|
||||
// * Doing this here since the object
|
||||
// * the permissions checks in the
|
||||
// * previous loop will mutate the data
|
||||
// * returned from the database
|
||||
if totalCount == uint32(len(pSearchResult.Result)) {
|
||||
totalCountType = 0 // * Has no more data. All possible results were returned
|
||||
} else {
|
||||
totalCountType = 1 // * Has more data. Not all possible results were returned
|
||||
}
|
||||
|
||||
// * Disables the TotalCount
|
||||
// *
|
||||
// * Only seen in struct revision 3 or
|
||||
// * NEX 4.0+
|
||||
if param.StructureVersion >= 3 || endpoint.LibraryVersions().DataStore.GreaterOrEqual("4.0.0") {
|
||||
if !param.TotalCountEnabled {
|
||||
totalCount = 0
|
||||
totalCountType = 3
|
||||
}
|
||||
}
|
||||
|
||||
pSearchResult.TotalCount = types.NewUInt32(totalCount)
|
||||
pSearchResult.TotalCountType = types.NewUInt8(totalCountType)
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
pSearchResult.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = datastore.ProtocolID
|
||||
rmcResponse.MethodID = datastore.MethodSearchObject
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterSearchObject != nil {
|
||||
go commonProtocol.OnAfterSearchObject(packet, param)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
7
globals/globals.go
Normal file
7
globals/globals.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package common_globals
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/plogger-go"
|
||||
)
|
||||
|
||||
var Logger = plogger.NewLogger()
|
|
@ -1,231 +1,27 @@
|
|||
package common_globals
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"database/sql"
|
||||
"sync"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
|
||||
"golang.org/x/exp/slices"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
)
|
||||
|
||||
type CommonMatchmakeSession struct {
|
||||
GameMatchmakeSession *match_making_types.MatchmakeSession // * Used by the game, contains the current state of the MatchmakeSession
|
||||
SearchMatchmakeSession *match_making_types.MatchmakeSession // * Used by the server when searching for matches, contains the state of the MatchmakeSession during the search process for easy compares
|
||||
SearchCriteria []*match_making_types.MatchmakeSessionSearchCriteria // * Used by the server when searching for matches, contains the list of MatchmakeSessionSearchCriteria
|
||||
ConnectionIDs []uint32 // * Players in the room, referenced by their connection IDs. This is used instead of the PID in order to ensure we're talking to the correct client (in case of e.g. multiple logins)
|
||||
// MatchmakingManager manages a matchmaking instance
|
||||
type MatchmakingManager struct {
|
||||
Database *sql.DB
|
||||
Endpoint *nex.PRUDPEndPoint
|
||||
Mutex *sync.RWMutex
|
||||
GetUserFriendPIDs func(pid uint32) []uint32
|
||||
GetDetailedGatheringByID func(manager *MatchmakingManager, sourcePID uint64, gatheringID uint32) (types.RVType, string, *nex.Error)
|
||||
}
|
||||
|
||||
var Sessions map[uint32]*CommonMatchmakeSession
|
||||
|
||||
// GetSessionIndex returns a gathering ID which doesn't belong to any session
|
||||
func GetSessionIndex() uint32 {
|
||||
var gatheringID uint32 = 1
|
||||
for gatheringID < math.MaxUint32 {
|
||||
// If the session does not exist, the gathering ID is empty and can be used
|
||||
if _, ok := Sessions[gatheringID]; !ok {
|
||||
return gatheringID
|
||||
}
|
||||
|
||||
gatheringID++
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// DeleteIndex removes a value from a slice with the given index
|
||||
func DeleteIndex(s []uint32, index int) []uint32 {
|
||||
s[index] = s[len(s)-1]
|
||||
return s[:len(s)-1]
|
||||
}
|
||||
|
||||
// FindOtherConnectionID searches a connection ID on the gathering that isn't the given one
|
||||
func FindOtherConnectionID(myConnectionID uint32, gathering uint32) uint32 {
|
||||
for _, connectionID := range Sessions[gathering].ConnectionIDs {
|
||||
if connectionID != myConnectionID {
|
||||
return connectionID
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// RemoveConnectionIDFromRoom removes a client from the gathering
|
||||
func RemoveConnectionIDFromRoom(clientConnectionID uint32, gathering uint32) {
|
||||
for index, connectionID := range Sessions[gathering].ConnectionIDs {
|
||||
if connectionID == clientConnectionID {
|
||||
Sessions[gathering].ConnectionIDs = DeleteIndex(Sessions[gathering].ConnectionIDs, index)
|
||||
}
|
||||
}
|
||||
if len(Sessions[gathering].ConnectionIDs) == 0 {
|
||||
delete(Sessions, gathering)
|
||||
// NewMatchmakingManager returns a new MatchmakingManager
|
||||
func NewMatchmakingManager(endpoint *nex.PRUDPEndPoint, db *sql.DB) *MatchmakingManager {
|
||||
return &MatchmakingManager{
|
||||
Endpoint: endpoint,
|
||||
Database: db,
|
||||
Mutex: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
// FindClientSession searches the gathering where the client is on
|
||||
func FindClientSession(clientConnectionID uint32) uint32 {
|
||||
for gatheringID := range Sessions {
|
||||
for _, connectionID := range Sessions[gatheringID].ConnectionIDs {
|
||||
if connectionID == clientConnectionID {
|
||||
return gatheringID
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// RemoveConnectionIDFromAllSessions removes a client from every session
|
||||
func RemoveConnectionIDFromAllSessions(clientConnectionID uint32) {
|
||||
foundSession := FindClientSession(clientConnectionID)
|
||||
if foundSession != 0 {
|
||||
RemoveConnectionIDFromRoom(clientConnectionID, uint32(foundSession))
|
||||
}
|
||||
}
|
||||
|
||||
// SearchGatheringWithMatchmakeSession finds a gathering that matches with a MatchmakeSession
|
||||
func SearchGatheringWithMatchmakeSession(searchMatchmakeSession *match_making_types.MatchmakeSession) uint32 {
|
||||
// This portion finds any sessions that match the search session. It does not care about anything beyond that, such as if the match is already full. This is handled below.
|
||||
candidateSessionIndexes := make([]uint32, 0, len(Sessions))
|
||||
for index, session := range Sessions {
|
||||
if session.SearchMatchmakeSession.Equals(searchMatchmakeSession) {
|
||||
candidateSessionIndexes = append(candidateSessionIndexes, index)
|
||||
}
|
||||
}
|
||||
for _, sessionIndex := range candidateSessionIndexes {
|
||||
sessionToCheck := Sessions[sessionIndex]
|
||||
if len(sessionToCheck.ConnectionIDs) >= int(sessionToCheck.GameMatchmakeSession.MaximumParticipants) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !sessionToCheck.GameMatchmakeSession.OpenParticipation {
|
||||
continue
|
||||
}
|
||||
return sessionIndex // Found a match
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// SearchGatheringWithSearchCriteria finds a gathering that matches with a MatchmakeSession
|
||||
func SearchGatheringWithSearchCriteria(lstSearchCriteria []*match_making_types.MatchmakeSessionSearchCriteria, gameSpecificChecks func(requestSearchCriteria, sessionSearchCriteria *match_making_types.MatchmakeSessionSearchCriteria) bool) uint32 {
|
||||
// * This portion finds any sessions that match the search session. It does not care about anything beyond that, such as if the match is already full. This is handled below.
|
||||
candidateSessionIndexes := make([]uint32, 0, len(Sessions))
|
||||
|
||||
for index, session := range Sessions {
|
||||
if len(lstSearchCriteria) == len(session.SearchCriteria) {
|
||||
for criteriaIndex, sessionSearchCriteria := range session.SearchCriteria {
|
||||
requestSearchCriteria := lstSearchCriteria[criteriaIndex]
|
||||
|
||||
// * Check things like game specific attributes
|
||||
if gameSpecificChecks != nil && !gameSpecificChecks(lstSearchCriteria[criteriaIndex], sessionSearchCriteria) {
|
||||
continue
|
||||
}
|
||||
|
||||
if requestSearchCriteria.GameMode != "" && requestSearchCriteria.GameMode != sessionSearchCriteria.GameMode {
|
||||
continue
|
||||
}
|
||||
|
||||
if requestSearchCriteria.MinParticipants != "" {
|
||||
split := strings.Split(requestSearchCriteria.MinParticipants, ",")
|
||||
minStr, maxStr := split[0], split[1]
|
||||
|
||||
if minStr != "" {
|
||||
min, err := strconv.Atoi(minStr)
|
||||
if err != nil {
|
||||
// TODO - We don't have a logger here
|
||||
continue
|
||||
}
|
||||
|
||||
if session.SearchMatchmakeSession.MinimumParticipants < uint16(min) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if maxStr != "" {
|
||||
max, err := strconv.Atoi(maxStr)
|
||||
if err != nil {
|
||||
// TODO - We don't have a logger here
|
||||
continue
|
||||
}
|
||||
|
||||
if session.SearchMatchmakeSession.MinimumParticipants > uint16(max) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if requestSearchCriteria.MaxParticipants != "" {
|
||||
split := strings.Split(requestSearchCriteria.MaxParticipants, ",")
|
||||
minStr := split[0]
|
||||
maxStr := ""
|
||||
|
||||
if len(split) > 1 {
|
||||
maxStr = split[1]
|
||||
}
|
||||
|
||||
if minStr != "" {
|
||||
min, err := strconv.Atoi(minStr)
|
||||
if err != nil {
|
||||
// TODO - We don't have a logger here
|
||||
continue
|
||||
}
|
||||
|
||||
if session.SearchMatchmakeSession.MaximumParticipants < uint16(min) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if maxStr != "" {
|
||||
max, err := strconv.Atoi(maxStr)
|
||||
if err != nil {
|
||||
// TODO - We don't have a logger here
|
||||
continue
|
||||
}
|
||||
|
||||
if session.SearchMatchmakeSession.MaximumParticipants > uint16(max) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
candidateSessionIndexes = append(candidateSessionIndexes, index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// * Further filter the candidate sessions
|
||||
for _, sessionIndex := range candidateSessionIndexes {
|
||||
sessionToCheck := Sessions[sessionIndex]
|
||||
if len(sessionToCheck.ConnectionIDs) >= int(sessionToCheck.GameMatchmakeSession.MaximumParticipants) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !sessionToCheck.GameMatchmakeSession.OpenParticipation {
|
||||
continue
|
||||
}
|
||||
|
||||
return sessionIndex // * Found a match
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// AddPlayersToSession updates the given sessions state to include the provided connection IDs
|
||||
// Returns a NEX error code if failed
|
||||
func AddPlayersToSession(session *CommonMatchmakeSession, connectionIDs []uint32) (error, uint32) {
|
||||
if (len(session.ConnectionIDs) + len(connectionIDs)) > int(session.GameMatchmakeSession.Gathering.MaximumParticipants) {
|
||||
return fmt.Errorf("Gathering %d is full", session.GameMatchmakeSession.Gathering.ID), nex.Errors.RendezVous.SessionFull
|
||||
}
|
||||
|
||||
for _, connectedID := range connectionIDs {
|
||||
if slices.Contains(session.ConnectionIDs, connectedID) {
|
||||
return fmt.Errorf("Connection ID %d is already in gathering %d", connectedID, session.GameMatchmakeSession.Gathering.ID), nex.Errors.RendezVous.AlreadyParticipatedGathering
|
||||
}
|
||||
|
||||
session.ConnectionIDs = append(session.ConnectionIDs, connectedID)
|
||||
|
||||
session.GameMatchmakeSession.ParticipationCount += 1
|
||||
}
|
||||
|
||||
return nil, 0
|
||||
}
|
||||
|
|
187
globals/utils.go
Normal file
187
globals/utils.go
Normal file
|
@ -0,0 +1,187 @@
|
|||
package common_globals
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/constants"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
match_making_constants "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/constants"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
var OutgoingCallID *nex.Counter[uint32] = nex.NewCounter[uint32](0)
|
||||
|
||||
// DeleteIndex removes a value from a slice with the given index
|
||||
func DeleteIndex(s []uint32, index int) []uint32 {
|
||||
s[index] = s[len(s)-1]
|
||||
return s[:len(s)-1]
|
||||
}
|
||||
|
||||
// RemoveDuplicates removes duplicate entries on a slice
|
||||
func RemoveDuplicates[T comparable](sliceList []T) []T {
|
||||
// * Extracted from https://stackoverflow.com/a/66751055
|
||||
allKeys := make(map[T]bool)
|
||||
list := []T{}
|
||||
for _, item := range sliceList {
|
||||
if _, value := allKeys[item]; !value {
|
||||
allKeys[item] = true
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// CheckValidGathering checks if a Gathering is valid
|
||||
func CheckValidGathering(gathering match_making_types.Gathering) bool {
|
||||
if len(gathering.Description) > 256 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckValidMatchmakeSession checks if a MatchmakeSession is valid
|
||||
func CheckValidMatchmakeSession(matchmakeSession match_making_types.MatchmakeSession) bool {
|
||||
if !CheckValidGathering(matchmakeSession.Gathering) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(matchmakeSession.Attributes) != 6 {
|
||||
return false
|
||||
}
|
||||
|
||||
if matchmakeSession.ProgressScore > 100 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(matchmakeSession.UserPassword) > 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
// * Except for UserPassword, all strings must have a length lower than 256
|
||||
if len(matchmakeSession.CodeWord) > 256 {
|
||||
return false
|
||||
}
|
||||
|
||||
// * All buffers must have a length lower than 512
|
||||
if len(matchmakeSession.ApplicationBuffer) > 512 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(matchmakeSession.SessionKey) > 512 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckValidPersistentGathering checks if a PersistentGathering is valid
|
||||
func CheckValidPersistentGathering(persistentGathering match_making_types.PersistentGathering) bool {
|
||||
if !CheckValidGathering(persistentGathering.Gathering) {
|
||||
return false
|
||||
}
|
||||
|
||||
// * Only allow normal and password-protected community types
|
||||
if uint32(persistentGathering.CommunityType) != uint32(match_making_constants.PersistentGatheringTypeOpen) && uint32(persistentGathering.CommunityType) != uint32(match_making_constants.PersistentGatheringTypePasswordLocked) {
|
||||
return false
|
||||
}
|
||||
|
||||
// * The UserPassword from a MatchmakeSession can be up to 32 characters, assuming the same here
|
||||
//
|
||||
// TODO - IS this actually the case?
|
||||
if len(persistentGathering.Password) > 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(persistentGathering.Attribs) != 6 {
|
||||
return false
|
||||
}
|
||||
|
||||
// * All buffers must have a length lower than 512
|
||||
if len(persistentGathering.ApplicationBuffer) > 512 {
|
||||
return false
|
||||
}
|
||||
|
||||
// * Check that the participation dates are within bounds of the current date
|
||||
currentTime := types.NewDateTime(0).Now()
|
||||
|
||||
if persistentGathering.ParticipationStartDate != 0 && persistentGathering.ParticipationStartDate > currentTime {
|
||||
return false
|
||||
}
|
||||
|
||||
if persistentGathering.ParticipationEndDate != 0 && persistentGathering.ParticipationEndDate < currentTime {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CanJoinMatchmakeSession checks if a PID is allowed to join a matchmake session
|
||||
func CanJoinMatchmakeSession(manager *MatchmakingManager, pid types.PID, matchmakeSession match_making_types.MatchmakeSession) *nex.Error {
|
||||
// TODO - Is this the right error?
|
||||
if !matchmakeSession.OpenParticipation {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
// * Only allow friends
|
||||
// TODO - This won't work on Switch!
|
||||
if matchmakeSession.ParticipationPolicy == 98 {
|
||||
if manager.GetUserFriendPIDs == nil {
|
||||
Logger.Warning("Missing GetUserFriendPIDs!")
|
||||
return nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
friendList := manager.GetUserFriendPIDs(uint32(pid))
|
||||
if !slices.Contains(friendList, uint32(matchmakeSession.OwnerPID)) {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.NotFriend, "change_error")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendNotificationEvent sends a notification event to the specified targets
|
||||
func SendNotificationEvent(endpoint *nex.PRUDPEndPoint, event notifications_types.NotificationEvent, targets []uint64) {
|
||||
server := endpoint.Server
|
||||
stream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
event.WriteTo(stream)
|
||||
|
||||
rmcRequest := nex.NewRMCRequest(endpoint)
|
||||
rmcRequest.ProtocolID = notifications.ProtocolID
|
||||
rmcRequest.CallID = OutgoingCallID.Next()
|
||||
rmcRequest.MethodID = notifications.MethodProcessNotificationEvent
|
||||
rmcRequest.Parameters = stream.Bytes()
|
||||
|
||||
rmcRequestBytes := rmcRequest.Bytes()
|
||||
|
||||
for _, pid := range targets {
|
||||
target := endpoint.FindConnectionByPID(pid)
|
||||
if target == nil {
|
||||
Logger.Warning("Client not found")
|
||||
continue
|
||||
}
|
||||
|
||||
var messagePacket nex.PRUDPPacketInterface
|
||||
|
||||
if target.DefaultPRUDPVersion == 0 {
|
||||
messagePacket, _ = nex.NewPRUDPPacketV0(server, target, nil)
|
||||
} else {
|
||||
messagePacket, _ = nex.NewPRUDPPacketV1(server, target, nil)
|
||||
}
|
||||
|
||||
messagePacket.SetType(constants.DataPacket)
|
||||
messagePacket.AddFlag(constants.PacketFlagNeedsAck)
|
||||
messagePacket.AddFlag(constants.PacketFlagReliable)
|
||||
messagePacket.SetSourceVirtualPortStreamType(target.StreamType)
|
||||
messagePacket.SetSourceVirtualPortStreamID(endpoint.StreamID)
|
||||
messagePacket.SetDestinationVirtualPortStreamType(target.StreamType)
|
||||
messagePacket.SetDestinationVirtualPortStreamID(target.StreamID)
|
||||
messagePacket.SetPayload(rmcRequestBytes)
|
||||
|
||||
server.Send(messagePacket)
|
||||
}
|
||||
}
|
40
go.mod
40
go.mod
|
@ -1,20 +1,40 @@
|
|||
module github.com/PretendoNetwork/nex-protocols-common-go
|
||||
module github.com/PretendoNetwork/nex-protocols-common-go/v2
|
||||
|
||||
go 1.19
|
||||
go 1.22.1
|
||||
|
||||
toolchain go1.22.4
|
||||
|
||||
require (
|
||||
github.com/PretendoNetwork/nex-go v1.0.37
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.51
|
||||
github.com/PretendoNetwork/nex-go/v2 v2.1.1
|
||||
github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.0
|
||||
github.com/PretendoNetwork/plogger-go v1.0.4
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
|
||||
github.com/PretendoNetwork/pq-extended v1.0.0
|
||||
github.com/minio/minio-go/v7 v7.0.83
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/dolthub/maphash v0.1.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/goccy/go-json v0.10.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jwalton/go-supportscolor v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/lxzan/gws v1.8.8 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
)
|
||||
|
|
84
go.sum
84
go.sum
|
@ -1,29 +1,75 @@
|
|||
github.com/PretendoNetwork/nex-go v1.0.37 h1:ibKdTEeIsCtBPPm1+VFsUMfrFng1LnjGCYbIB1FzSY4=
|
||||
github.com/PretendoNetwork/nex-go v1.0.37/go.mod h1:v3dYsytdk7VvZOqplKwVqQOPa9heaq2V4DOAzCwyYNE=
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.51 h1:1vsYc+MF93+LlnQQuv2PmIPNys5vio/5e+3uLD+GTiw=
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.51/go.mod h1:BGvDT9wxj7drgej+qfIpPZwnc+V2C6GaK9QdzX1rdac=
|
||||
github.com/PretendoNetwork/nex-go/v2 v2.1.1 h1:OI2W14lIw2V2kItV2c/57UGLrT6PV5CuhyU07VAAki4=
|
||||
github.com/PretendoNetwork/nex-go/v2 v2.1.1/go.mod h1:3LyJzsv3AataJW8D0binp15Q8ZH22MWTYly1VNtXi64=
|
||||
github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.0 h1:abbCXgYN9icR9hmGV9GkuiOvg92+WBeVTnvtiAU06QU=
|
||||
github.com/PretendoNetwork/nex-protocols-go/v2 v2.2.0/go.mod h1:+soBHmwX6ixGxj6cphLuCvfJqxcZPuowc/5e7Qi9Bz0=
|
||||
github.com/PretendoNetwork/plogger-go v1.0.4 h1:PF7xHw9eDRHH+RsAP9tmAE7fG0N0p6H4iPwHKnsoXwc=
|
||||
github.com/PretendoNetwork/plogger-go v1.0.4/go.mod h1:7kD6M4vPq1JL4LTuPg6kuB1OvUBOwQOtAvTaUwMbwvU=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/PretendoNetwork/pq-extended v1.0.0 h1:GHZ0hLvCvmYKQPTV9I9XtTx8J1iB5Z9CEnfW2tUpsYg=
|
||||
github.com/PretendoNetwork/pq-extended v1.0.0/go.mod h1:bq6Ai+3lG4/M0iamUBt2Uzi5vL/nYy1a1Ar2ow9NDF0=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
|
||||
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
|
||||
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jwalton/go-supportscolor v1.2.0 h1:g6Ha4u7Vm3LIsQ5wmeBpS4gazu0UP1DRDE8y6bre4H8=
|
||||
github.com/jwalton/go-supportscolor v1.2.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lxzan/gws v1.8.8 h1:st193ZG8qN8sSw8/g/UituFhs7etmKzS7jUqhijg5wM=
|
||||
github.com/lxzan/gws v1.8.8/go.mod h1:FcGeRMB7HwGuTvMLR24ku0Zx0p6RXqeKASeMc4VYgi4=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.83 h1:W4Kokksvlz3OKf3OqIlzDNKd4MERlC2oN8YptwJ0+GA=
|
||||
github.com/minio/minio-go/v7 v7.0.83/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e h1:dCWirM5F3wMY+cmRda/B1BiPsFtmzXqV9b0hLWtVBMs=
|
||||
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e/go.mod h1:9leZcVcItj6m9/CfHY5Em/iBrCz7js8LcRQGTKEEv2M=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7 h1:N9RLxaR65C36i26BUIpzPXGy2f6pQ7wisu2bawbKNqg=
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7/go.mod h1:4ub2EKgF1MAhTjoOCTU4b9uLMsAweHEa89aRrfAypXA=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
52
match-making-ext/end_participation.go
Normal file
52
match-making-ext/end_participation.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package match_making_ext
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
match_making_ext "github.com/PretendoNetwork/nex-protocols-go/v2/match-making-ext"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) endParticipation(err error, packet nex.PacketInterface, callID uint32, idGathering types.UInt32, strMessage types.String) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
if len(strMessage) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
nexError := database.EndGatheringParticipation(commonProtocol.manager, uint32(idGathering), connection, string(strMessage))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
retval := types.NewBool(true)
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
retval.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = match_making_ext.ProtocolID
|
||||
rmcResponse.MethodID = match_making_ext.MethodEndParticipation
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterEndParticipation != nil {
|
||||
go commonProtocol.OnAfterEndParticipation(packet, idGathering, strMessage)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
32
match-making-ext/protocol.go
Normal file
32
match-making-ext/protocol.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package match_making_ext
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_ext "github.com/PretendoNetwork/nex-protocols-go/v2/match-making-ext"
|
||||
)
|
||||
|
||||
type CommonProtocol struct {
|
||||
endpoint nex.EndpointInterface
|
||||
protocol match_making_ext.Interface
|
||||
manager *common_globals.MatchmakingManager
|
||||
OnAfterEndParticipation func(acket nex.PacketInterface, idGathering types.UInt32, strMessage types.String)
|
||||
}
|
||||
|
||||
// SetManager defines the matchmaking manager to be used by the common protocol
|
||||
func (commonProtocol *CommonProtocol) SetManager(manager *common_globals.MatchmakingManager) {
|
||||
commonProtocol.manager = manager
|
||||
}
|
||||
|
||||
// NewCommonProtocol returns a new CommonProtocol
|
||||
func NewCommonProtocol(protocol match_making_ext.Interface) *CommonProtocol {
|
||||
commonProtocol := &CommonProtocol{
|
||||
endpoint: protocol.Endpoint(),
|
||||
protocol: protocol,
|
||||
}
|
||||
|
||||
protocol.SetHandlerEndParticipation(commonProtocol.endParticipation)
|
||||
|
||||
return commonProtocol
|
||||
}
|
165
match-making/database/disconnect_participant.go
Normal file
165
match-making/database/disconnect_participant.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// DisconnectParticipant disconnects a participant from all non-persistent gatherings
|
||||
func DisconnectParticipant(manager *common_globals.MatchmakingManager, connection *nex.PRUDPConnection) {
|
||||
var nexError *nex.Error
|
||||
|
||||
rows, err := manager.Database.Query(`SELECT id, owner_pid, host_pid, min_participants, max_participants, participation_policy, policy_argument, flags, state, description, type, participants FROM matchmaking.gatherings WHERE $1 = ANY(participants) AND registered=true`, connection.PID())
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
gathering := match_making_types.NewGathering()
|
||||
var gatheringType string
|
||||
var participants []uint64
|
||||
|
||||
err = rows.Scan(
|
||||
&gathering.ID,
|
||||
&gathering.OwnerPID,
|
||||
&gathering.HostPID,
|
||||
&gathering.MinimumParticipants,
|
||||
&gathering.MaximumParticipants,
|
||||
&gathering.ParticipationPolicy,
|
||||
&gathering.PolicyArgument,
|
||||
&gathering.Flags,
|
||||
&gathering.State,
|
||||
&gathering.Description,
|
||||
&gatheringType,
|
||||
pqextended.Array(&participants),
|
||||
)
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// * If the gathering is a PersistentGathering and the gathering isn't set to leave when disconnecting, ignore and continue
|
||||
//
|
||||
// TODO - Is the match_making.GatheringFlags.PersistentGathering check correct here?
|
||||
if uint32(gathering.Flags) & match_making.GatheringFlags.PersistentGathering != 0 || (gatheringType == "PersistentGathering" && uint32(gathering.Flags) & match_making.GatheringFlags.PersistentGatheringLeaveParticipation == 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
// * Since the participant is leaving, override the participant list to avoid sending notifications to them
|
||||
participants, nexError = RemoveParticipantFromGathering(manager, uint32(gathering.ID), uint64(connection.PID()))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
nexError = tracking.LogDisconnectGathering(manager.Database, connection.PID(), uint32(gathering.ID), participants)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// * If the gathering is a persistent gathering and allows zero users, only remove the participant from the gathering
|
||||
if uint32(gathering.Flags) & (match_making.GatheringFlags.PersistentGathering | match_making.GatheringFlags.PersistentGatheringAllowZeroUsers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(participants) == 0 {
|
||||
// * There are no more participants, so we only have to unregister the gathering
|
||||
// * Since the participant is disconnecting, we don't send notification events
|
||||
nexError = UnregisterGathering(manager, connection.PID(), uint32(gathering.ID))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ownerPID := uint64(gathering.OwnerPID)
|
||||
if connection.PID().Equals(gathering.OwnerPID) {
|
||||
// * This flag tells the server to change the matchmake session owner if they disconnect
|
||||
// * If the flag is not set, delete the session
|
||||
// * More info: https://nintendo-wiki.pretendo.network/docs/nex/protocols/match-making/types#flags
|
||||
if uint32(gathering.Flags) & match_making.GatheringFlags.DisconnectChangeOwner == 0 {
|
||||
nexError = UnregisterGathering(manager, connection.PID(), uint32(gathering.ID))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.GatheringUnregistered
|
||||
subtype := notifications.NotificationSubTypes.GatheringUnregistered.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = gathering.ID
|
||||
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, common_globals.RemoveDuplicates(participants))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ownerPID, nexError = MigrateGatheringOwnership(manager, connection, gathering, participants)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// * If the host has disconnected, set the owner as the new host. We can guarantee that the ownerPID is not zero,
|
||||
// * since otherwise the gathering would have been unregistered by MigrateGatheringOwnership
|
||||
if connection.PID().Equals(gathering.HostPID) && ownerPID != 0 {
|
||||
nexError = UpdateSessionHost(manager, uint32(gathering.ID), types.NewPID(ownerPID), types.NewPID(ownerPID))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
} else {
|
||||
category := notifications.NotificationCategories.HostChanged
|
||||
subtype := notifications.NotificationSubTypes.HostChanged.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = gathering.ID
|
||||
|
||||
// TODO - Should the notification actually be sent to all participants?
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, common_globals.RemoveDuplicates(participants))
|
||||
|
||||
nexError = tracking.LogChangeHost(manager.Database, connection.PID(), uint32(gathering.ID), gathering.HostPID, types.NewPID(ownerPID))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.Participation
|
||||
subtype := notifications.NotificationSubTypes.Participation.Disconnected
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = gathering.ID
|
||||
oEvent.Param2 = types.NewUInt32(uint32(connection.PID())) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
|
||||
var participantEndedTargets []uint64
|
||||
|
||||
// * When the VerboseParticipants or VerboseParticipantsEx flags are set, all participant notification events are sent to everyone
|
||||
if uint32(gathering.Flags) & (match_making.GatheringFlags.VerboseParticipants | match_making.GatheringFlags.VerboseParticipantsEx) != 0 {
|
||||
participantEndedTargets = common_globals.RemoveDuplicates(participants)
|
||||
} else {
|
||||
participantEndedTargets = []uint64{uint64(gathering.OwnerPID)}
|
||||
}
|
||||
|
||||
// * Only send the notification event to the owner
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, participantEndedTargets)
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
126
match-making/database/end_gathering_participation.go
Normal file
126
match-making/database/end_gathering_participation.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
// EndGatheringParticipation ends the participation of a connection within a gathering and performs any additional handling required
|
||||
func EndGatheringParticipation(manager *common_globals.MatchmakingManager, gatheringID uint32, connection *nex.PRUDPConnection, message string) *nex.Error {
|
||||
gathering, _, participants, _, nexError := FindGatheringByID(manager, gatheringID)
|
||||
if nexError != nil {
|
||||
return nexError
|
||||
}
|
||||
|
||||
// TODO - Is this the right error?
|
||||
if !slices.Contains(participants, uint64(connection.PID())) {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.NotParticipatedGathering, "change_error")
|
||||
}
|
||||
|
||||
newParticipants, nexError := RemoveParticipantFromGathering(manager, gatheringID, uint64(connection.PID()))
|
||||
if nexError != nil {
|
||||
return nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogLeaveGathering(manager.Database, connection.PID(), gatheringID, newParticipants)
|
||||
if nexError != nil {
|
||||
return nexError
|
||||
}
|
||||
|
||||
// * If the gathering is a persistent gathering and allows zero users, only remove the participant from the gathering
|
||||
if uint32(gathering.Flags) & (match_making.GatheringFlags.PersistentGathering | match_making.GatheringFlags.PersistentGatheringAllowZeroUsers) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(newParticipants) == 0 {
|
||||
// * There are no more participants, so we just unregister the gathering
|
||||
return UnregisterGathering(manager, connection.PID(), gatheringID)
|
||||
}
|
||||
|
||||
var ownerPID uint64 = uint64(gathering.OwnerPID)
|
||||
if connection.PID().Equals(gathering.OwnerPID) {
|
||||
// * This flag tells the server to change the matchmake session owner if they disconnect
|
||||
// * If the flag is not set, delete the session
|
||||
// * More info: https://nintendo-wiki.pretendo.network/docs/nex/protocols/match-making/types#flags
|
||||
if uint32(gathering.Flags) & match_making.GatheringFlags.DisconnectChangeOwner == 0 {
|
||||
nexError = UnregisterGathering(manager, connection.PID(), gatheringID)
|
||||
if nexError != nil {
|
||||
return nexError
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.GatheringUnregistered
|
||||
subtype := notifications.NotificationSubTypes.GatheringUnregistered.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = types.NewUInt32(gatheringID)
|
||||
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, common_globals.RemoveDuplicates(newParticipants))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
ownerPID, nexError = MigrateGatheringOwnership(manager, connection, gathering, newParticipants)
|
||||
if nexError != nil {
|
||||
return nexError
|
||||
}
|
||||
}
|
||||
|
||||
// * If the host has disconnected, set the owner as the new host. We can guarantee that the ownerPID is not zero,
|
||||
// * since otherwise the gathering would have been unregistered by MigrateGatheringOwnership
|
||||
if connection.PID().Equals(gathering.HostPID) && ownerPID != 0 {
|
||||
nexError = UpdateSessionHost(manager, gatheringID, types.NewPID(ownerPID), types.NewPID(ownerPID))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
return nexError
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.HostChanged
|
||||
subtype := notifications.NotificationSubTypes.HostChanged.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = types.NewUInt32(gatheringID)
|
||||
|
||||
// TODO - Should the notification actually be sent to all participants?
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, common_globals.RemoveDuplicates(participants))
|
||||
|
||||
nexError = tracking.LogChangeHost(manager.Database, connection.PID(), gatheringID, gathering.HostPID, types.NewPID(ownerPID))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
return nexError
|
||||
}
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.Participation
|
||||
subtype := notifications.NotificationSubTypes.Participation.Ended
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = types.NewUInt32(gatheringID)
|
||||
oEvent.Param2 = types.NewUInt32(uint32((connection.PID()))) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
oEvent.StrParam = types.NewString(message)
|
||||
|
||||
var participationEndedTargets []uint64
|
||||
|
||||
// * When the VerboseParticipants or VerboseParticipantsEx flags are set, all participant notification events are sent to everyone
|
||||
if uint32(gathering.Flags) & (match_making.GatheringFlags.VerboseParticipants | match_making.GatheringFlags.VerboseParticipantsEx) != 0 {
|
||||
participationEndedTargets = common_globals.RemoveDuplicates(participants)
|
||||
} else {
|
||||
participationEndedTargets = []uint64{uint64(gathering.OwnerPID)}
|
||||
}
|
||||
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, participationEndedTargets)
|
||||
|
||||
return nil
|
||||
}
|
49
match-making/database/find_gathering_by_id.go
Normal file
49
match-making/database/find_gathering_by_id.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// FindGatheringByID finds a gathering on a database with the given ID. Returns the gathering, its type, the participant list and the started time
|
||||
func FindGatheringByID(manager *common_globals.MatchmakingManager, id uint32) (match_making_types.Gathering, string, []uint64, types.DateTime, *nex.Error) {
|
||||
row := manager.Database.QueryRow(`SELECT owner_pid, host_pid, min_participants, max_participants, participation_policy, policy_argument, flags, state, description, type, participants, started_time FROM matchmaking.gatherings WHERE id=$1 AND registered=true`, id)
|
||||
|
||||
gathering := match_making_types.NewGathering()
|
||||
var gatheringType string
|
||||
var participants []uint64
|
||||
var startedTime time.Time
|
||||
|
||||
err := row.Scan(
|
||||
&gathering.OwnerPID,
|
||||
&gathering.HostPID,
|
||||
&gathering.MinimumParticipants,
|
||||
&gathering.MaximumParticipants,
|
||||
&gathering.ParticipationPolicy,
|
||||
&gathering.PolicyArgument,
|
||||
&gathering.Flags,
|
||||
&gathering.State,
|
||||
&gathering.Description,
|
||||
&gatheringType,
|
||||
pqextended.Array(&participants),
|
||||
&startedTime,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return gathering, "", nil, types.NewDateTime(0), nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, err.Error())
|
||||
} else {
|
||||
return gathering, "", nil, types.NewDateTime(0), nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
gathering.ID = types.NewUInt32(id)
|
||||
startedDateTime := types.NewDateTime(0)
|
||||
|
||||
return gathering, gatheringType, participants, startedDateTime.FromTimestamp(startedTime), nil
|
||||
}
|
21
match-making/database/get_detailed_gathering_by_id.go
Normal file
21
match-making/database/get_detailed_gathering_by_id.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// GetDetailedGatheringByID returns a Gathering as an RVType by its gathering ID
|
||||
func GetDetailedGatheringByID(manager *common_globals.MatchmakingManager, sourcePID uint64, gatheringID uint32) (types.RVType, string, *nex.Error) {
|
||||
gathering, gatheringType, _, _, nexError := FindGatheringByID(manager, gatheringID)
|
||||
if nexError != nil {
|
||||
return nil, "", nexError
|
||||
}
|
||||
|
||||
if gatheringType != "Gathering" {
|
||||
return nil, "", nex.NewError(nex.ResultCodes.Core.Exception, "change_error")
|
||||
}
|
||||
|
||||
return gathering, gatheringType, nil
|
||||
}
|
121
match-making/database/join_gathering.go
Normal file
121
match-making/database/join_gathering.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// JoinGathering joins participants from the same connection into a gathering. Returns the new number of participants
|
||||
func JoinGathering(manager *common_globals.MatchmakingManager, gatheringID uint32, connection *nex.PRUDPConnection, vacantParticipants uint16, joinMessage string) (uint32, *nex.Error) {
|
||||
// * vacantParticipants represents the total number of participants that are joining (including the main participant)
|
||||
// * Prevent underflow below if vacantParticipants is set to zero
|
||||
if vacantParticipants == 0 {
|
||||
vacantParticipants = 1
|
||||
}
|
||||
|
||||
var ownerPID uint64
|
||||
var maxParticipants uint32
|
||||
var flags uint32
|
||||
var participants []uint64
|
||||
err := manager.Database.QueryRow(`SELECT owner_pid, max_participants, flags, participants FROM matchmaking.gatherings WHERE id=$1`, gatheringID).Scan(&ownerPID, &maxParticipants, &flags, pqextended.Array(&participants))
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
} else {
|
||||
return 0, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if maxParticipants != 0 {
|
||||
if uint32(len(participants)) + uint32(vacantParticipants) > maxParticipants {
|
||||
return 0, nex.NewError(nex.ResultCodes.RendezVous.SessionFull, "change_error")
|
||||
}
|
||||
}
|
||||
|
||||
if slices.Contains(participants, uint64(connection.PID())) {
|
||||
return 0, nex.NewError(nex.ResultCodes.RendezVous.AlreadyParticipatedGathering, "change_error")
|
||||
}
|
||||
|
||||
var newParticipants []uint64
|
||||
|
||||
// * Additional participants are represented by duplicating the main participant PID on the array
|
||||
for range vacantParticipants {
|
||||
newParticipants = append(newParticipants, uint64(connection.PID()))
|
||||
}
|
||||
|
||||
var totalParticipants []uint64 = append(newParticipants, participants...)
|
||||
|
||||
// * We have already checked that the gathering exists above, so we don't have to check the rows affected on sql.Result
|
||||
_, err = manager.Database.Exec(`UPDATE matchmaking.gatherings SET participants=$1 WHERE id=$2`, pqextended.Array(totalParticipants), gatheringID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
} else {
|
||||
return 0, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
nexError := tracking.LogJoinGathering(manager.Database, connection.PID(), gatheringID, newParticipants, totalParticipants)
|
||||
if nexError != nil {
|
||||
return 0, nexError
|
||||
}
|
||||
|
||||
var participantJoinedTargets []uint64
|
||||
|
||||
// * When the VerboseParticipants or VerboseParticipantsEx flags are set, all participant notification events are sent to everyone
|
||||
if flags & (match_making.GatheringFlags.VerboseParticipants | match_making.GatheringFlags.VerboseParticipantsEx) != 0 {
|
||||
participantJoinedTargets = common_globals.RemoveDuplicates(totalParticipants)
|
||||
} else {
|
||||
// * If the new participant is the same as the owner, then we are creating a new gathering.
|
||||
// * We don't need to send notification events in that case
|
||||
if uint64(connection.PID()) == ownerPID {
|
||||
return uint32(len(totalParticipants)), nil
|
||||
}
|
||||
|
||||
participantJoinedTargets = []uint64{ownerPID}
|
||||
}
|
||||
|
||||
notificationCategory := notifications.NotificationCategories.Participation
|
||||
notificationSubtype := notifications.NotificationSubTypes.Participation.NewParticipant
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID()
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(notificationCategory, notificationSubtype))
|
||||
oEvent.Param1 = types.NewUInt32(gatheringID)
|
||||
oEvent.Param2 = types.NewUInt32(uint32(connection.PID())) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
oEvent.StrParam = types.NewString(joinMessage)
|
||||
oEvent.Param3 = types.NewUInt32(uint32(len(totalParticipants)))
|
||||
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, participantJoinedTargets)
|
||||
|
||||
// * This flag also sends a recap of all currently connected players on the gathering to the participant that is connecting
|
||||
if flags & match_making.GatheringFlags.VerboseParticipantsEx != 0 {
|
||||
// TODO - Should this actually be deduplicated?
|
||||
for _, participant := range common_globals.RemoveDuplicates(participants) {
|
||||
notificationCategory := notifications.NotificationCategories.Participation
|
||||
notificationSubtype := notifications.NotificationSubTypes.Participation.NewParticipant
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID()
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(notificationCategory, notificationSubtype))
|
||||
oEvent.Param1 = types.NewUInt32(gatheringID)
|
||||
oEvent.Param2 = types.NewUInt32(uint32(participant)) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
oEvent.StrParam = types.NewString(joinMessage)
|
||||
oEvent.Param3 = types.NewUInt32(uint32(len(totalParticipants)))
|
||||
|
||||
// * Send the notification to the joining participant
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, []uint64{uint64(connection.PID())})
|
||||
}
|
||||
}
|
||||
|
||||
return uint32(len(totalParticipants)), nil
|
||||
}
|
140
match-making/database/join_gathering_with_participants.go
Normal file
140
match-making/database/join_gathering_with_participants.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/v2/match-making/constants"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// JoinGatheringWithParticipants joins participants into a gathering. Returns the new number of participants
|
||||
func JoinGatheringWithParticipants(manager *common_globals.MatchmakingManager, gatheringID uint32, connection *nex.PRUDPConnection, additionalParticipants []types.PID, joinMessage string, joinMatchmakeSessionBehavior constants.JoinMatchmakeSessionBehavior) (uint32, *nex.Error) {
|
||||
var ownerPID uint64
|
||||
var maxParticipants uint32
|
||||
var flags uint32
|
||||
var oldParticipants []uint64
|
||||
err := manager.Database.QueryRow(`SELECT owner_pid, max_participants, flags, participants FROM matchmaking.gatherings WHERE id=$1`, gatheringID).Scan(&ownerPID, &maxParticipants, &flags, pqextended.Array(&oldParticipants))
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
} else {
|
||||
return 0, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if uint32(len(oldParticipants) + 1 + len(additionalParticipants)) > maxParticipants {
|
||||
return 0, nex.NewError(nex.ResultCodes.RendezVous.SessionFull, "change_error")
|
||||
}
|
||||
|
||||
var newParticipants []uint64
|
||||
|
||||
// * If joinMatchmakeSessionBehavior is set to 1, we check if the caller is already joined into the session
|
||||
if joinMatchmakeSessionBehavior == constants.JoinMatchmakeSessionBehaviorImAlreadyJoined {
|
||||
if !slices.Contains(oldParticipants, uint64(connection.PID())) {
|
||||
return 0, nex.NewError(nex.ResultCodes.RendezVous.NotParticipatedGathering, "change_error")
|
||||
}
|
||||
} else {
|
||||
if slices.Contains(oldParticipants, uint64(connection.PID())) {
|
||||
return 0, nex.NewError(nex.ResultCodes.RendezVous.AlreadyParticipatedGathering, "change_error")
|
||||
}
|
||||
|
||||
// * Only include the caller as a new participant when they aren't joined
|
||||
newParticipants = []uint64{uint64(connection.PID())}
|
||||
}
|
||||
|
||||
for _, participant := range additionalParticipants {
|
||||
newParticipants = append(newParticipants, uint64(participant))
|
||||
}
|
||||
|
||||
participants := append(oldParticipants, newParticipants...)
|
||||
|
||||
// * We have already checked that the gathering exists above, so we don't have to check the rows affected on sql.Result
|
||||
_, err = manager.Database.Exec(`UPDATE matchmaking.gatherings SET participants=$1 WHERE id=$2`, pqextended.Array(participants), gatheringID)
|
||||
if err != nil {
|
||||
return 0, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
// NOTE - This will log even if no new participants are added
|
||||
nexError := tracking.LogJoinGathering(manager.Database, connection.PID(), gatheringID, newParticipants, participants)
|
||||
if nexError != nil {
|
||||
return 0, nexError
|
||||
}
|
||||
|
||||
var participantJoinedTargets []uint64
|
||||
|
||||
// * When the VerboseParticipants or the VerboseParticipantsEx flags are set, all participant notification events are sent to everyone
|
||||
if flags & (match_making.GatheringFlags.VerboseParticipants | match_making.GatheringFlags.VerboseParticipantsEx) != 0 {
|
||||
participantJoinedTargets = common_globals.RemoveDuplicates(participants)
|
||||
} else {
|
||||
participantJoinedTargets = []uint64{ownerPID}
|
||||
}
|
||||
|
||||
// * Send the switch SwitchGathering to the new participants first
|
||||
for _, participant := range common_globals.RemoveDuplicates(newParticipants) {
|
||||
// * Don't send the SwitchGathering notification to the participant that requested the join
|
||||
if uint64(connection.PID()) == participant {
|
||||
continue
|
||||
}
|
||||
|
||||
notificationCategory := notifications.NotificationCategories.SwitchGathering
|
||||
notificationSubtype := notifications.NotificationSubTypes.SwitchGathering.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID()
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(notificationCategory, notificationSubtype))
|
||||
oEvent.Param1 = types.NewUInt32(gatheringID)
|
||||
oEvent.Param2 = types.NewUInt32(uint32(participant)) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
|
||||
// * Send the notification to the participant
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, []uint64{participant})
|
||||
}
|
||||
|
||||
for _, participant := range newParticipants {
|
||||
// * If the new participant is the same as the owner, then we are creating a new gathering.
|
||||
// * We don't need to send the new participant notification event in that case
|
||||
if flags & (match_making.GatheringFlags.VerboseParticipants | match_making.GatheringFlags.VerboseParticipantsEx) != 0 || uint64(connection.PID()) != ownerPID {
|
||||
notificationCategory := notifications.NotificationCategories.Participation
|
||||
notificationSubtype := notifications.NotificationSubTypes.Participation.NewParticipant
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID()
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(notificationCategory, notificationSubtype))
|
||||
oEvent.Param1 = types.NewUInt32(gatheringID)
|
||||
oEvent.Param2 = types.NewUInt32(uint32(participant)) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
oEvent.StrParam = types.NewString(joinMessage)
|
||||
oEvent.Param3 = types.NewUInt32(uint32(len(participants)))
|
||||
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, participantJoinedTargets)
|
||||
}
|
||||
|
||||
// * This flag also sends a recap of all currently connected players on the gathering to the participant that is connecting
|
||||
if flags & match_making.GatheringFlags.VerboseParticipantsEx != 0 {
|
||||
// TODO - Should this actually be deduplicated?
|
||||
for _, oldParticipant := range common_globals.RemoveDuplicates(oldParticipants) {
|
||||
notificationCategory := notifications.NotificationCategories.Participation
|
||||
notificationSubtype := notifications.NotificationSubTypes.Participation.NewParticipant
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID()
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(notificationCategory, notificationSubtype))
|
||||
oEvent.Param1 = types.NewUInt32(gatheringID)
|
||||
oEvent.Param2 = types.NewUInt32(uint32(oldParticipant)) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
oEvent.StrParam = types.NewString(joinMessage)
|
||||
oEvent.Param3 = types.NewUInt32(uint32(len(participants)))
|
||||
|
||||
// * Send the notification to the joining participant
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, []uint64{participant})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uint32(len(participants)), nil
|
||||
}
|
75
match-making/database/migrate_gathering_ownership.go
Normal file
75
match-making/database/migrate_gathering_ownership.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
// MigrateGatheringOwnership switches the owner of the gathering with a different one
|
||||
func MigrateGatheringOwnership(manager *common_globals.MatchmakingManager, connection *nex.PRUDPConnection, gathering match_making_types.Gathering, participants []uint64) (uint64, *nex.Error) {
|
||||
var nexError *nex.Error
|
||||
var uniqueParticipants []uint64 = common_globals.RemoveDuplicates(participants)
|
||||
var newOwner uint64
|
||||
for _, participant := range uniqueParticipants {
|
||||
if participant != uint64(gathering.OwnerPID) {
|
||||
newOwner = participant
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// * We couldn't find a new owner, so we unregister the gathering
|
||||
if newOwner == 0 {
|
||||
nexError = UnregisterGathering(manager, connection.PID(), uint32(gathering.ID))
|
||||
if nexError != nil {
|
||||
return 0, nexError
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.GatheringUnregistered
|
||||
subtype := notifications.NotificationSubTypes.GatheringUnregistered.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = gathering.ID
|
||||
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, uniqueParticipants)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
oldOwner := gathering.OwnerPID.Copy().(types.PID)
|
||||
|
||||
// * Set the new owner
|
||||
gathering.OwnerPID = types.NewPID(newOwner)
|
||||
|
||||
nexError = UpdateSessionHost(manager, uint32(gathering.ID), gathering.OwnerPID, gathering.HostPID)
|
||||
if nexError != nil {
|
||||
return 0, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogChangeOwner(manager.Database, connection.PID(), uint32(gathering.ID), oldOwner, gathering.OwnerPID)
|
||||
if nexError != nil {
|
||||
return 0, nexError
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.OwnershipChanged
|
||||
subtype := notifications.NotificationSubTypes.OwnershipChanged.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = gathering.ID
|
||||
oEvent.Param2 = types.NewUInt32(uint32(newOwner)) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
|
||||
// TODO - StrParam doesn't have this value on some servers
|
||||
// * https://github.com/kinnay/NintendoClients/issues/101
|
||||
// * unixTime := time.Now()
|
||||
// * oEvent.StrParam = strconv.FormatInt(unixTime.UnixMicro(), 10)
|
||||
|
||||
common_globals.SendNotificationEvent(connection.Endpoint().(*nex.PRUDPEndPoint), oEvent, uniqueParticipants)
|
||||
return newOwner, nil
|
||||
}
|
68
match-making/database/register_gathering.go
Normal file
68
match-making/database/register_gathering.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
)
|
||||
|
||||
// RegisterGathering registers a new gathering on the database. No participants are added
|
||||
func RegisterGathering(manager *common_globals.MatchmakingManager, ownerPID types.PID, hostPID types.PID, gathering *match_making_types.Gathering, gatheringType string) (types.DateTime, *nex.Error) {
|
||||
startedTime := types.NewDateTime(0).Now()
|
||||
var gatheringID uint32
|
||||
|
||||
err := manager.Database.QueryRow(`INSERT INTO matchmaking.gatherings (
|
||||
owner_pid,
|
||||
host_pid,
|
||||
min_participants,
|
||||
max_participants,
|
||||
participation_policy,
|
||||
policy_argument,
|
||||
flags,
|
||||
state,
|
||||
description,
|
||||
type,
|
||||
started_time
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9,
|
||||
$10,
|
||||
$11
|
||||
) RETURNING id`,
|
||||
ownerPID,
|
||||
hostPID,
|
||||
gathering.MinimumParticipants,
|
||||
gathering.MaximumParticipants,
|
||||
gathering.ParticipationPolicy,
|
||||
gathering.PolicyArgument,
|
||||
gathering.Flags,
|
||||
gathering.State,
|
||||
gathering.Description,
|
||||
gatheringType,
|
||||
startedTime.Standard(),
|
||||
).Scan(&gatheringID)
|
||||
if err != nil {
|
||||
return types.NewDateTime(0), nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
gathering.ID = types.NewUInt32(gatheringID)
|
||||
|
||||
nexError := tracking.LogRegisterGathering(manager.Database, ownerPID, uint32(gathering.ID))
|
||||
if nexError != nil {
|
||||
return types.NewDateTime(0), nexError
|
||||
}
|
||||
|
||||
gathering.OwnerPID = ownerPID.Copy().(types.PID)
|
||||
gathering.HostPID = hostPID.Copy().(types.PID)
|
||||
|
||||
return startedTime, nil
|
||||
}
|
24
match-making/database/remove_participant_from_gathering.go
Normal file
24
match-making/database/remove_participant_from_gathering.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// RemoveParticipantFromGathering removes a participant from a gathering. Returns the new list of participants
|
||||
func RemoveParticipantFromGathering(manager *common_globals.MatchmakingManager, gatheringID uint32, participant uint64) ([]uint64, *nex.Error) {
|
||||
var newParticipants []uint64
|
||||
err := manager.Database.QueryRow(`UPDATE matchmaking.gatherings SET participants=array_remove(participants, $1) WHERE id=$2 RETURNING participants`, participant, gatheringID).Scan(pqextended.Array(&newParticipants))
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
} else {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return newParticipants, nil
|
||||
}
|
32
match-making/database/unregister_gathering.go
Normal file
32
match-making/database/unregister_gathering.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
)
|
||||
|
||||
// UnregisterGathering unregisters a given gathering on a database
|
||||
func UnregisterGathering(manager *common_globals.MatchmakingManager, sourcePID types.PID, id uint32) *nex.Error {
|
||||
result, err := manager.Database.Exec(`UPDATE matchmaking.gatherings SET registered=false WHERE id=$1`, id)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
}
|
||||
|
||||
nexError := tracking.LogUnregisterGathering(manager.Database, sourcePID, id)
|
||||
if nexError != nil {
|
||||
return nexError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
26
match-making/database/update_session_host.go
Normal file
26
match-making/database/update_session_host.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// UpdateSessionHost updates the owner and host PID of the session
|
||||
func UpdateSessionHost(manager *common_globals.MatchmakingManager, gatheringID uint32, ownerPID types.PID, hostPID types.PID) *nex.Error {
|
||||
result, err := manager.Database.Exec(`UPDATE matchmaking.gatherings SET owner_pid=$1, host_pid=$2 WHERE id=$3`, ownerPID, hostPID, gatheringID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
51
match-making/find_by_single_id.go
Normal file
51
match-making/find_by_single_id.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package matchmaking
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) findBySingleID(err error, packet nex.PacketInterface, callID uint32, id types.UInt32) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
gathering, _, nexError := commonProtocol.manager.GetDetailedGatheringByID(commonProtocol.manager, uint64(connection.PID()), uint32(id))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
bResult := types.NewBool(true)
|
||||
pGathering := match_making_types.NewGatheringHolder()
|
||||
pGathering.Object = gathering.Copy().(match_making_types.GatheringInterface)
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
bResult.WriteTo(rmcResponseStream)
|
||||
pGathering.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = match_making.ProtocolID
|
||||
rmcResponse.MethodID = match_making.MethodFindBySingleID
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterFindBySingleID != nil {
|
||||
go commonProtocol.OnAfterFindBySingleID(packet, id)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
61
match-making/get_session_urls.go
Normal file
61
match-making/get_session_urls.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package matchmaking
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) getSessionURLs(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
gathering, _, participants, _, nexError := database.FindGatheringByID(commonProtocol.manager, uint32(gid))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
if !slices.Contains(participants, uint64(connection.PID())) {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
host := endpoint.FindConnectionByPID(uint64(gathering.HostPID))
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
// * If no host was found, return an empty list of station URLs
|
||||
if host == nil {
|
||||
common_globals.Logger.Error("Host client not found")
|
||||
stationURLs := types.NewList[types.StationURL]()
|
||||
stationURLs.WriteTo(rmcResponseStream)
|
||||
} else {
|
||||
host.StationURLs.WriteTo(rmcResponseStream)
|
||||
}
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = match_making.ProtocolID
|
||||
rmcResponse.MethodID = match_making.MethodGetSessionURLs
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterGetSessionURLs != nil {
|
||||
go commonProtocol.OnAfterGetSessionURLs(packet, gid)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
174
match-making/protocol.go
Normal file
174
match-making/protocol.go
Normal file
|
@ -0,0 +1,174 @@
|
|||
package matchmaking
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
_ "github.com/PretendoNetwork/nex-protocols-go/v2"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
)
|
||||
|
||||
type CommonProtocol struct {
|
||||
endpoint *nex.PRUDPEndPoint
|
||||
protocol match_making.Interface
|
||||
manager *common_globals.MatchmakingManager
|
||||
OnAfterUnregisterGathering func(packet nex.PacketInterface, idGathering types.UInt32)
|
||||
OnAfterFindBySingleID func(packet nex.PacketInterface, id types.UInt32)
|
||||
OnAfterUpdateSessionURL func(packet nex.PacketInterface, idGathering types.UInt32, strURL types.String)
|
||||
OnAfterUpdateSessionHostV1 func(packet nex.PacketInterface, gid types.UInt32)
|
||||
OnAfterGetSessionURLs func(packet nex.PacketInterface, gid types.UInt32)
|
||||
OnAfterUpdateSessionHost func(packet nex.PacketInterface, gid types.UInt32, isMigrateOwner types.Bool)
|
||||
}
|
||||
|
||||
// SetManager defines the matchmaking manager to be used by the common protocol
|
||||
func (commonProtocol *CommonProtocol) SetManager(manager *common_globals.MatchmakingManager) {
|
||||
var err error
|
||||
|
||||
commonProtocol.manager = manager
|
||||
|
||||
manager.GetDetailedGatheringByID = database.GetDetailedGatheringByID
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE SCHEMA IF NOT EXISTS matchmaking`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS matchmaking.gatherings (
|
||||
id bigserial PRIMARY KEY,
|
||||
owner_pid numeric(10),
|
||||
host_pid numeric(10),
|
||||
min_participants integer,
|
||||
max_participants integer,
|
||||
participation_policy bigint,
|
||||
policy_argument bigint,
|
||||
flags bigint,
|
||||
state bigint,
|
||||
description text,
|
||||
registered boolean NOT NULL DEFAULT true,
|
||||
type text NOT NULL DEFAULT '',
|
||||
started_time timestamp,
|
||||
participants numeric(10)[] NOT NULL DEFAULT array[]::numeric(10)[]
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE SCHEMA IF NOT EXISTS tracking`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.register_gathering (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
gathering_id bigint
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.join_gathering (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
gathering_id bigint,
|
||||
new_participants numeric(10)[] NOT NULL DEFAULT array[]::numeric(10)[],
|
||||
total_participants numeric(10)[] NOT NULL DEFAULT array[]::numeric(10)[]
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.leave_gathering (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
gathering_id bigint,
|
||||
total_participants numeric(10)[] NOT NULL DEFAULT array[]::numeric(10)[]
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.disconnect_gathering (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
gathering_id bigint,
|
||||
total_participants numeric(10)[] NOT NULL DEFAULT array[]::numeric(10)[]
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.unregister_gathering (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
gathering_id bigint
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.change_host (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
gathering_id bigint,
|
||||
old_host_pid numeric(10),
|
||||
new_host_pid numeric(10)
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.change_owner (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
gathering_id bigint,
|
||||
old_owner_pid numeric(10),
|
||||
new_owner_pid numeric(10)
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// NewCommonProtocol returns a new CommonProtocol
|
||||
func NewCommonProtocol(protocol match_making.Interface) *CommonProtocol {
|
||||
endpoint := protocol.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol := &CommonProtocol{
|
||||
endpoint: endpoint,
|
||||
protocol: protocol,
|
||||
}
|
||||
|
||||
protocol.SetHandlerUnregisterGathering(commonProtocol.unregisterGathering)
|
||||
protocol.SetHandlerFindBySingleID(commonProtocol.findBySingleID)
|
||||
protocol.SetHandlerUpdateSessionURL(commonProtocol.updateSessionURL)
|
||||
protocol.SetHandlerUpdateSessionHostV1(commonProtocol.updateSessionHostV1)
|
||||
protocol.SetHandlerGetSessionURLs(commonProtocol.getSessionURLs)
|
||||
protocol.SetHandlerUpdateSessionHost(commonProtocol.updateSessionHost)
|
||||
|
||||
endpoint.OnConnectionEnded(func(connection *nex.PRUDPConnection) {
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
database.DisconnectParticipant(commonProtocol.manager, connection)
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
})
|
||||
|
||||
return commonProtocol
|
||||
}
|
33
match-making/tracking/log_change_host.go
Normal file
33
match-making/tracking/log_change_host.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
)
|
||||
|
||||
// LogChangeHost logs a host change event on the given database
|
||||
func LogChangeHost(db *sql.DB, sourcePID types.PID, gatheringID uint32, oldHostPID types.PID, newHostPID types.PID) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.change_host (
|
||||
date,
|
||||
source_pid,
|
||||
gathering_id,
|
||||
old_host_pid,
|
||||
new_host_pid
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
)`, eventTime, sourcePID, gatheringID, oldHostPID, newHostPID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
33
match-making/tracking/log_change_owner.go
Normal file
33
match-making/tracking/log_change_owner.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
)
|
||||
|
||||
// LogChangeOwner logs an owner change event on the given database
|
||||
func LogChangeOwner(db *sql.DB, sourcePID types.PID, gatheringID uint32, oldOwnerPID types.PID, newOwnerPID types.PID) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.change_owner (
|
||||
date,
|
||||
source_pid,
|
||||
gathering_id,
|
||||
old_owner_pid,
|
||||
new_owner_pid
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
)`, eventTime, sourcePID, gatheringID, oldOwnerPID, newOwnerPID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
32
match-making/tracking/log_disconnect_gathering.go
Normal file
32
match-making/tracking/log_disconnect_gathering.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// LogDisconnectGathering logs a gathering disconnect event on the given database
|
||||
func LogDisconnectGathering(db *sql.DB, pid types.PID, gatheringID uint32, totalParticipants []uint64) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.disconnect_gathering (
|
||||
date,
|
||||
source_pid,
|
||||
gathering_id,
|
||||
total_participants
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4
|
||||
)`, eventTime, pid, gatheringID, pqextended.Array(totalParticipants))
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
34
match-making/tracking/log_join_gathering.go
Normal file
34
match-making/tracking/log_join_gathering.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// LogJoinGathering logs a gathering join event on the given database
|
||||
func LogJoinGathering(db *sql.DB, sourcePID types.PID, gatheringID uint32, newParticipants []uint64, totalParticipants []uint64) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.join_gathering (
|
||||
date,
|
||||
source_pid,
|
||||
gathering_id,
|
||||
new_participants,
|
||||
total_participants
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
)`, eventTime, sourcePID, gatheringID, pqextended.Array(newParticipants), pqextended.Array(totalParticipants))
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
32
match-making/tracking/log_leave_gathering.go
Normal file
32
match-making/tracking/log_leave_gathering.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// LogLeaveGathering logs a gathering leave event on the given database
|
||||
func LogLeaveGathering(db *sql.DB, pid types.PID, gatheringID uint32, totalParticipants []uint64) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.leave_gathering (
|
||||
date,
|
||||
source_pid,
|
||||
gathering_id,
|
||||
total_participants
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4
|
||||
)`, eventTime, pid, gatheringID, pqextended.Array(totalParticipants))
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
29
match-making/tracking/log_register_gathering.go
Normal file
29
match-making/tracking/log_register_gathering.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
)
|
||||
|
||||
// LogRegisterGathering logs a gathering registration event on the given database
|
||||
func LogRegisterGathering(db *sql.DB, sourcePID types.PID, gatheringID uint32) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.register_gathering (
|
||||
date,
|
||||
source_pid,
|
||||
gathering_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3
|
||||
)`, eventTime, sourcePID, gatheringID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
29
match-making/tracking/log_unregister_gathering.go
Normal file
29
match-making/tracking/log_unregister_gathering.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
)
|
||||
|
||||
// LogUnregisterGathering logs a gathering registration event on the given database
|
||||
func LogUnregisterGathering(db *sql.DB, sourcePID types.PID, gatheringID uint32) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.unregister_gathering (
|
||||
date,
|
||||
source_pid,
|
||||
gathering_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3
|
||||
)`, eventTime, sourcePID, gatheringID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
70
match-making/unregister_gathering.go
Normal file
70
match-making/unregister_gathering.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package matchmaking
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) unregisterGathering(err error, packet nex.PacketInterface, callID uint32, idGathering types.UInt32) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
gathering, _, participants, _, nexError := database.FindGatheringByID(commonProtocol.manager, uint32(idGathering))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
if !gathering.OwnerPID.Equals(connection.PID()) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
nexError = database.UnregisterGathering(commonProtocol.manager, connection.PID(), uint32(idGathering))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.GatheringUnregistered
|
||||
subtype := notifications.NotificationSubTypes.GatheringUnregistered.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID().Copy().(types.PID)
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = idGathering
|
||||
|
||||
common_globals.SendNotificationEvent(endpoint, oEvent, common_globals.RemoveDuplicates(participants))
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
retval := types.NewBool(true)
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
retval.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = match_making.ProtocolID
|
||||
rmcResponse.MethodID = match_making.MethodUnregisterGathering
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterUnregisterGathering != nil {
|
||||
go commonProtocol.OnAfterUnregisterGathering(packet, idGathering)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
102
match-making/update_session_host.go
Normal file
102
match-making/update_session_host.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package matchmaking
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) updateSessionHost(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32, isMigrateOwner types.Bool) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
gathering, _, participants, _, nexError := database.FindGatheringByID(commonProtocol.manager, uint32(gid))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
if !slices.Contains(participants, uint64(connection.PID())) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
if !isMigrateOwner {
|
||||
nexError = database.UpdateSessionHost(commonProtocol.manager, uint32(gid), gathering.OwnerPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogChangeHost(commonProtocol.manager.Database, connection.PID(), uint32(gid), gathering.HostPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
} else {
|
||||
if uint32(gathering.Flags) & match_making.GatheringFlags.ParticipantsChangeOwner == 0 {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.InvalidOperation, "change_error")
|
||||
}
|
||||
|
||||
nexError = database.UpdateSessionHost(commonProtocol.manager, uint32(gid), connection.PID(), connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogChangeHost(commonProtocol.manager.Database, connection.PID(), uint32(gid), gathering.HostPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogChangeOwner(commonProtocol.manager.Database, connection.PID(), uint32(gid), gathering.OwnerPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.OwnershipChanged
|
||||
subtype := notifications.NotificationSubTypes.OwnershipChanged.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID()
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = gid
|
||||
oEvent.Param2 = types.NewUInt32(uint32(connection.PID())) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
|
||||
// TODO - StrParam doesn't have this value on some servers
|
||||
// * https://github.com/kinnay/NintendoClients/issues/101
|
||||
// * unixTime := time.Now()
|
||||
// * oEvent.StrParam = strconv.FormatInt(unixTime.UnixMicro(), 10)
|
||||
|
||||
common_globals.SendNotificationEvent(endpoint, oEvent, common_globals.RemoveDuplicates(participants))
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = match_making.ProtocolID
|
||||
rmcResponse.MethodID = match_making.MethodUpdateSessionHost
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterUpdateSessionHost != nil {
|
||||
go commonProtocol.OnAfterUpdateSessionHost(packet, gid, isMigrateOwner)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
98
match-making/update_session_host_v1.go
Normal file
98
match-making/update_session_host_v1.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package matchmaking
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) updateSessionHostV1(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
gathering, _, participants, _, nexError := database.FindGatheringByID(commonProtocol.manager, uint32(gid))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
if !slices.Contains(participants, uint64(connection.PID())) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
if uint32(gathering.Flags) & match_making.GatheringFlags.ParticipantsChangeOwner == 0 {
|
||||
nexError = database.UpdateSessionHost(commonProtocol.manager, uint32(gid), gathering.OwnerPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogChangeHost(commonProtocol.manager.Database, connection.PID(), uint32(gid), gathering.HostPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
} else {
|
||||
nexError = database.UpdateSessionHost(commonProtocol.manager, uint32(gid), connection.PID(), connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogChangeHost(commonProtocol.manager.Database, connection.PID(), uint32(gid), gathering.HostPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogChangeOwner(commonProtocol.manager.Database, connection.PID(), uint32(gid), gathering.OwnerPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
category := notifications.NotificationCategories.OwnershipChanged
|
||||
subtype := notifications.NotificationSubTypes.OwnershipChanged.None
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = connection.PID()
|
||||
oEvent.Type = types.NewUInt32(notifications.BuildNotificationType(category, subtype))
|
||||
oEvent.Param1 = gid
|
||||
oEvent.Param2 = types.NewUInt32(uint32(connection.PID())) // TODO - This assumes a legacy client. Will not work on the Switch
|
||||
|
||||
// TODO - StrParam doesn't have this value on some servers
|
||||
// * https://github.com/kinnay/NintendoClients/issues/101
|
||||
// * unixTime := time.Now()
|
||||
// * oEvent.StrParam = strconv.FormatInt(unixTime.UnixMicro(), 10)
|
||||
|
||||
common_globals.SendNotificationEvent(endpoint, oEvent, common_globals.RemoveDuplicates(participants))
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = match_making.ProtocolID
|
||||
rmcResponse.MethodID = match_making.MethodUpdateSessionHostV1
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterUpdateSessionHostV1 != nil {
|
||||
go commonProtocol.OnAfterUpdateSessionHostV1(packet, gid)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
70
match-making/update_session_url.go
Normal file
70
match-making/update_session_url.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package matchmaking
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/tracking"
|
||||
match_making "github.com/PretendoNetwork/nex-protocols-go/v2/match-making"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) updateSessionURL(err error, packet nex.PacketInterface, callID uint32, idGathering types.UInt32, strURL types.String) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
gathering, _, participants, _, nexError := database.FindGatheringByID(commonProtocol.manager, uint32(idGathering))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
if !slices.Contains(participants, uint64(connection.PID())) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
// TODO - Mario Kart 7 seems to set an empty strURL. What does that do if it's actually set?
|
||||
|
||||
// * Only update the host
|
||||
nexError = database.UpdateSessionHost(commonProtocol.manager, uint32(idGathering), gathering.OwnerPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogChangeHost(commonProtocol.manager.Database, connection.PID(), uint32(idGathering), gathering.HostPID, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
retval := types.NewBool(true)
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
retval.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = match_making.ProtocolID
|
||||
rmcResponse.MethodID = match_making.MethodGetSessionURLs
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterUpdateSessionURL != nil {
|
||||
go commonProtocol.OnAfterUpdateSessionURL(packet, idGathering, strURL)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
|
@ -1,141 +1,99 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/notifications/types"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
database "github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
)
|
||||
|
||||
func autoMatchmake_Postpone(err error, client *nex.Client, callID uint32, anyGathering *nex.DataHolder, message string) uint32 {
|
||||
func (commonProtocol *CommonProtocol) autoMatchmakePostpone(err error, packet nex.PacketInterface, callID uint32, anyGathering match_making_types.GatheringHolder, message types.String) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.CleanupSearchMatchmakeSession == nil {
|
||||
common_globals.Logger.Warning("MatchmakeExtension::AutoMatchmake_Postpone missing CleanupSearchMatchmakeSession!")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
server := commonMatchmakeExtensionProtocol.server
|
||||
if commonMatchmakeExtensionProtocol.cleanupSearchMatchmakeSessionHandler == nil {
|
||||
logger.Warning("MatchmakeExtension::AutoMatchmake_Postpone missing CleanupSearchMatchmakeSessionHandler!")
|
||||
return nex.Errors.Core.Exception
|
||||
if len(message) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
// A client may disconnect from a session without leaving reliably,
|
||||
// so let's make sure the client is removed from the session
|
||||
common_globals.RemoveConnectionIDFromAllSessions(client.ConnectionID())
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
var matchmakeSession *match_making_types.MatchmakeSession
|
||||
anyGatheringDataType := anyGathering.TypeName()
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
if anyGatheringDataType == "MatchmakeSession" {
|
||||
matchmakeSession = anyGathering.ObjectData().(*match_making_types.MatchmakeSession)
|
||||
// * A client may disconnect from a session without leaving reliably,
|
||||
// * so let's make sure the client is removed from the session
|
||||
database.EndMatchmakeSessionsParticipation(commonProtocol.manager, connection)
|
||||
|
||||
var matchmakeSession match_making_types.MatchmakeSession
|
||||
|
||||
if anyGathering.Object.ObjectID().Equals(types.NewString("MatchmakeSession")) {
|
||||
matchmakeSession = anyGathering.Object.(match_making_types.MatchmakeSession)
|
||||
} else {
|
||||
logger.Critical("Non-MatchmakeSession DataType?!")
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Critical("Non-MatchmakeSession DataType?!")
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
searchMatchmakeSession := matchmakeSession.Copy().(*match_making_types.MatchmakeSession)
|
||||
commonMatchmakeExtensionProtocol.cleanupSearchMatchmakeSessionHandler(searchMatchmakeSession)
|
||||
sessionIndex := common_globals.SearchGatheringWithMatchmakeSession(searchMatchmakeSession)
|
||||
if sessionIndex == 0 {
|
||||
sessionIndex = common_globals.GetSessionIndex()
|
||||
// This should in theory be impossible, as there aren't enough PIDs creating sessions to fill the uint32 limit.
|
||||
// If we ever get here, we must be not deleting sessions properly
|
||||
if sessionIndex == 0 {
|
||||
logger.Critical("No gatherings available!")
|
||||
return nex.Errors.RendezVous.LimitExceeded
|
||||
}
|
||||
|
||||
session := common_globals.CommonMatchmakeSession{
|
||||
SearchMatchmakeSession: searchMatchmakeSession,
|
||||
GameMatchmakeSession: matchmakeSession,
|
||||
}
|
||||
|
||||
common_globals.Sessions[sessionIndex] = &session
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.ID = sessionIndex
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.OwnerPID = client.PID()
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.HostPID = client.PID()
|
||||
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.StartedTime = nex.NewDateTime(0)
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.StartedTime.UTC()
|
||||
if !common_globals.CheckValidMatchmakeSession(matchmakeSession) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
common_globals.Sessions[sessionIndex].ConnectionIDs = append(common_globals.Sessions[sessionIndex].ConnectionIDs, client.ConnectionID())
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.ParticipationCount = uint32(len(common_globals.Sessions[sessionIndex].ConnectionIDs))
|
||||
searchMatchmakeSession := matchmakeSession.Copy().(match_making_types.MatchmakeSession)
|
||||
commonProtocol.CleanupSearchMatchmakeSession(&searchMatchmakeSession)
|
||||
resultSession, nexError := database.FindMatchmakeSession(commonProtocol.manager, connection, searchMatchmakeSession)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(server)
|
||||
matchmakeDataHolder := nex.NewDataHolder()
|
||||
matchmakeDataHolder.SetTypeName("MatchmakeSession")
|
||||
matchmakeDataHolder.SetObjectData(common_globals.Sessions[sessionIndex].GameMatchmakeSession)
|
||||
rmcResponseStream.WriteDataHolder(matchmakeDataHolder)
|
||||
if resultSession == nil {
|
||||
newMatchmakeSession := searchMatchmakeSession.Copy().(match_making_types.MatchmakeSession)
|
||||
resultSession = &newMatchmakeSession
|
||||
nexError = database.CreateMatchmakeSession(commonProtocol.manager, connection, resultSession)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
}
|
||||
|
||||
participants, nexError := database.JoinMatchmakeSession(commonProtocol.manager, *resultSession, connection, 1, string(message))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
resultSession.ParticipationCount = types.NewUInt32(participants)
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
matchmakeDataHolder := match_making_types.NewGatheringHolder()
|
||||
matchmakeDataHolder.Object = resultSession.Copy().(match_making_types.GatheringInterface)
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
matchmakeDataHolder.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(matchmake_extension.MethodAutoMatchmakePostpone, rmcResponseBody)
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodAutoMatchmakePostpone
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
var responsePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
responsePacket, _ = nex.NewPacketV0(client, nil)
|
||||
responsePacket.SetVersion(0)
|
||||
} else {
|
||||
responsePacket, _ = nex.NewPacketV1(client, nil)
|
||||
responsePacket.SetVersion(1)
|
||||
if commonProtocol.OnAfterAutoMatchmakePostpone != nil {
|
||||
go commonProtocol.OnAfterAutoMatchmakePostpone(packet, anyGathering, message)
|
||||
}
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(responsePacket)
|
||||
|
||||
rmcMessage := nex.NewRMCRequest()
|
||||
rmcMessage.SetProtocolID(notifications.ProtocolID)
|
||||
rmcMessage.SetCallID(0xffff0000 + callID)
|
||||
rmcMessage.SetMethodID(notifications.MethodProcessNotificationEvent)
|
||||
|
||||
category := notifications.NotificationCategories.Participation
|
||||
subtype := notifications.NotificationSubTypes.Participation.NewParticipant
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.HostPID
|
||||
oEvent.Type = notifications.BuildNotificationType(category, subtype)
|
||||
oEvent.Param1 = sessionIndex
|
||||
oEvent.Param2 = client.PID()
|
||||
oEvent.StrParam = message
|
||||
|
||||
stream := nex.NewStreamOut(server)
|
||||
oEventBytes := oEvent.Bytes(stream)
|
||||
rmcMessage.SetParameters(oEventBytes)
|
||||
rmcMessageBytes := rmcMessage.Bytes()
|
||||
|
||||
targetClient := server.FindClientFromPID(uint32(common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.OwnerPID))
|
||||
|
||||
var messagePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
messagePacket, _ = nex.NewPacketV0(targetClient, nil)
|
||||
messagePacket.SetVersion(0)
|
||||
} else {
|
||||
messagePacket, _ = nex.NewPacketV1(targetClient, nil)
|
||||
messagePacket.SetVersion(1)
|
||||
}
|
||||
messagePacket.SetSource(0xA1)
|
||||
messagePacket.SetDestination(0xAF)
|
||||
messagePacket.SetType(nex.DataPacket)
|
||||
messagePacket.SetPayload(rmcMessageBytes)
|
||||
|
||||
messagePacket.AddFlag(nex.FlagNeedsAck)
|
||||
messagePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(messagePacket)
|
||||
|
||||
return 0
|
||||
return rmcResponse, nil
|
||||
}
|
||||
|
|
105
matchmake-extension/auto_matchmake_with_param_postpone.go
Normal file
105
matchmake-extension/auto_matchmake_with_param_postpone.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/v2/match-making/constants"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) autoMatchmakeWithParamPostpone(err error, packet nex.PacketInterface, callID uint32, autoMatchmakeParam match_making_types.AutoMatchmakeParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.CleanupMatchmakeSessionSearchCriterias == nil {
|
||||
common_globals.Logger.Warning("MatchmakeExtension::AutoMatchmakeWithParam_Postpone missing CleanupMatchmakeSessionSearchCriterias!")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
if !common_globals.CheckValidMatchmakeSession(autoMatchmakeParam.SourceMatchmakeSession) {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
if len(autoMatchmakeParam.JoinMessage) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
if autoMatchmakeParam.GIDForParticipationCheck != 0 {
|
||||
// * Check that all new participants are participating in the specified gathering ID
|
||||
nexError := database.CheckGatheringForParticipation(commonProtocol.manager, uint32(autoMatchmakeParam.GIDForParticipationCheck), append(autoMatchmakeParam.AdditionalParticipants, connection.PID()))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
}
|
||||
|
||||
// * A client may disconnect from a session without leaving reliably,
|
||||
// * so let's make sure the client is removed from the session
|
||||
database.EndMatchmakeSessionsParticipation(commonProtocol.manager, connection)
|
||||
|
||||
commonProtocol.CleanupMatchmakeSessionSearchCriterias(autoMatchmakeParam.LstSearchCriteria)
|
||||
|
||||
resultRange := types.NewResultRange()
|
||||
resultRange.Length = 1
|
||||
resultSessions, nexError := database.FindMatchmakeSessionBySearchCriteria(commonProtocol.manager, connection, autoMatchmakeParam.LstSearchCriteria, resultRange, &autoMatchmakeParam.SourceMatchmakeSession)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
var resultSession match_making_types.MatchmakeSession
|
||||
if len(resultSessions) == 0 {
|
||||
resultSession = autoMatchmakeParam.SourceMatchmakeSession.Copy().(match_making_types.MatchmakeSession)
|
||||
nexError = database.CreateMatchmakeSession(commonProtocol.manager, connection, &resultSession)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
} else {
|
||||
resultSession = resultSessions[0]
|
||||
|
||||
// TODO - What should really happen here?
|
||||
if resultSession.UserPasswordEnabled || resultSession.SystemPasswordEnabled {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
}
|
||||
|
||||
participants, nexError := database.JoinMatchmakeSessionWithParticipants(commonProtocol.manager, resultSession, connection, autoMatchmakeParam.AdditionalParticipants, string(autoMatchmakeParam.JoinMessage), constants.JoinMatchmakeSessionBehaviorJoinMyself)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
resultSession.ParticipationCount = types.NewUInt32(participants)
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
resultSession.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodAutoMatchmakeWithParamPostpone
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterAutoMatchmakeWithParamPostpone != nil {
|
||||
go commonProtocol.OnAfterAutoMatchmakeWithParamPostpone(packet, autoMatchmakeParam)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
|
@ -1,143 +1,118 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/notifications/types"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func autoMatchmakeWithSearchCriteria_Postpone(err error, client *nex.Client, callID uint32, lstSearchCriteria []*match_making_types.MatchmakeSessionSearchCriteria, anyGathering *nex.DataHolder, message string) uint32 {
|
||||
if commonMatchmakeExtensionProtocol.cleanupMatchmakeSessionSearchCriteriaHandler == nil {
|
||||
logger.Warning("MatchmakeExtension::AutoMatchmake_Postpone missing CleanupMatchmakeSessionSearchCriteriaHandler!")
|
||||
return nex.Errors.Core.NotImplemented
|
||||
func (commonProtocol *CommonProtocol) autoMatchmakeWithSearchCriteriaPostpone(err error, packet nex.PacketInterface, callID uint32, lstSearchCriteria types.List[match_making_types.MatchmakeSessionSearchCriteria], anyGathering match_making_types.GatheringHolder, strMessage types.String) (*nex.RMCMessage, *nex.Error) {
|
||||
if commonProtocol.CleanupMatchmakeSessionSearchCriterias == nil {
|
||||
common_globals.Logger.Warning("MatchmakeExtension::AutoMatchmakeWithSearchCriteria_Postpone missing CleanupMatchmakeSessionSearchCriterias!")
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.NotImplemented, "change_error")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
server := commonMatchmakeExtensionProtocol.server
|
||||
if len(strMessage) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
if len(lstSearchCriteria) > 2 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
// * A client may disconnect from a session without leaving reliably,
|
||||
// * so let's make sure the client is removed from the session
|
||||
common_globals.RemoveConnectionIDFromAllSessions(client.ConnectionID())
|
||||
database.EndMatchmakeSessionsParticipation(commonProtocol.manager, connection)
|
||||
|
||||
var matchmakeSession *match_making_types.MatchmakeSession
|
||||
anyGatheringDataType := anyGathering.TypeName()
|
||||
var matchmakeSession match_making_types.MatchmakeSession
|
||||
|
||||
if anyGatheringDataType == "MatchmakeSession" {
|
||||
matchmakeSession = anyGathering.ObjectData().(*match_making_types.MatchmakeSession)
|
||||
if anyGathering.Object.GatheringObjectID().Equals(types.NewString("MatchmakeSession")) {
|
||||
matchmakeSession = anyGathering.Object.(match_making_types.MatchmakeSession)
|
||||
} else {
|
||||
logger.Critical("Non-MatchmakeSession DataType?!")
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Critical("Non-MatchmakeSession DataType?!")
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonMatchmakeExtensionProtocol.cleanupMatchmakeSessionSearchCriteriaHandler(lstSearchCriteria)
|
||||
sessionIndex := common_globals.SearchGatheringWithSearchCriteria(lstSearchCriteria, commonMatchmakeExtensionProtocol.gameSpecificMatchmakeSessionSearcgCriteriaChecksHandler)
|
||||
if sessionIndex == 0 {
|
||||
sessionIndex = common_globals.GetSessionIndex()
|
||||
// * This should in theory be impossible, as there aren't enough PIDs creating sessions to fill the uint32 limit.
|
||||
// * If we ever get here, we must be not deleting sessions properly
|
||||
if sessionIndex == 0 {
|
||||
logger.Critical("No gatherings available!")
|
||||
return nex.Errors.RendezVous.LimitExceeded
|
||||
}
|
||||
|
||||
session := common_globals.CommonMatchmakeSession{
|
||||
SearchCriteria: lstSearchCriteria,
|
||||
GameMatchmakeSession: matchmakeSession,
|
||||
}
|
||||
|
||||
common_globals.Sessions[sessionIndex] = &session
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.ID = sessionIndex
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.OwnerPID = client.PID()
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.HostPID = client.PID()
|
||||
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.StartedTime = nex.NewDateTime(0)
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.StartedTime.UTC()
|
||||
matchmakeSession.SessionKey = make([]byte, 32)
|
||||
if !common_globals.CheckValidMatchmakeSession(matchmakeSession) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
common_globals.Sessions[sessionIndex].ConnectionIDs = append(common_globals.Sessions[sessionIndex].ConnectionIDs, client.ConnectionID())
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.ParticipationCount = uint32(len(common_globals.Sessions[sessionIndex].ConnectionIDs))
|
||||
commonProtocol.CleanupMatchmakeSessionSearchCriterias(lstSearchCriteria)
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(server)
|
||||
matchmakeDataHolder := nex.NewDataHolder()
|
||||
matchmakeDataHolder.SetTypeName("MatchmakeSession")
|
||||
matchmakeDataHolder.SetObjectData(common_globals.Sessions[sessionIndex].GameMatchmakeSession)
|
||||
rmcResponseStream.WriteDataHolder(matchmakeDataHolder)
|
||||
resultRange := types.NewResultRange()
|
||||
resultRange.Length = 1
|
||||
resultSessions, nexError := database.FindMatchmakeSessionBySearchCriteria(commonProtocol.manager, connection, lstSearchCriteria, resultRange, &matchmakeSession)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
var resultSession match_making_types.MatchmakeSession
|
||||
if len(resultSessions) == 0 {
|
||||
resultSession = matchmakeSession.Copy().(match_making_types.MatchmakeSession)
|
||||
nexError = database.CreateMatchmakeSession(commonProtocol.manager, connection, &resultSession)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
} else {
|
||||
resultSession = resultSessions[0]
|
||||
|
||||
// TODO - What should really happen here?
|
||||
if resultSession.UserPasswordEnabled || resultSession.SystemPasswordEnabled {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
}
|
||||
|
||||
var vacantParticipants uint16 = 1
|
||||
if len(lstSearchCriteria) > 0 {
|
||||
vacantParticipants = uint16(lstSearchCriteria[0].VacantParticipants)
|
||||
}
|
||||
|
||||
participants, nexError := database.JoinMatchmakeSession(commonProtocol.manager, resultSession, connection, vacantParticipants, string(strMessage))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
resultSession.ParticipationCount = types.NewUInt32(participants)
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
matchmakeDataHolder := match_making_types.NewGatheringHolder()
|
||||
matchmakeDataHolder.Object = resultSession.Copy().(match_making_types.GatheringInterface)
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
matchmakeDataHolder.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(matchmake_extension.MethodAutoMatchmakeWithSearchCriteriaPostpone, rmcResponseBody)
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodAutoMatchmakeWithSearchCriteriaPostpone
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
var responsePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
responsePacket, _ = nex.NewPacketV0(client, nil)
|
||||
responsePacket.SetVersion(0)
|
||||
} else {
|
||||
responsePacket, _ = nex.NewPacketV1(client, nil)
|
||||
responsePacket.SetVersion(1)
|
||||
if commonProtocol.OnAfterAutoMatchmakeWithSearchCriteriaPostpone != nil {
|
||||
go commonProtocol.OnAfterAutoMatchmakeWithSearchCriteriaPostpone(packet, lstSearchCriteria, anyGathering, strMessage)
|
||||
}
|
||||
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(responsePacket)
|
||||
|
||||
rmcMessage := nex.NewRMCRequest()
|
||||
rmcMessage.SetProtocolID(notifications.ProtocolID)
|
||||
rmcMessage.SetCallID(0xffff0000 + callID)
|
||||
rmcMessage.SetMethodID(notifications.MethodProcessNotificationEvent)
|
||||
|
||||
category := notifications.NotificationCategories.Participation
|
||||
subtype := notifications.NotificationSubTypes.Participation.NewParticipant
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.HostPID
|
||||
oEvent.Type = notifications.BuildNotificationType(category, subtype)
|
||||
oEvent.Param1 = sessionIndex
|
||||
oEvent.Param2 = client.PID()
|
||||
oEvent.StrParam = message
|
||||
|
||||
stream := nex.NewStreamOut(server)
|
||||
oEventBytes := oEvent.Bytes(stream)
|
||||
rmcMessage.SetParameters(oEventBytes)
|
||||
rmcMessageBytes := rmcMessage.Bytes()
|
||||
|
||||
targetClient := server.FindClientFromPID(uint32(common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.OwnerPID))
|
||||
|
||||
var messagePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
messagePacket, _ = nex.NewPacketV0(targetClient, nil)
|
||||
messagePacket.SetVersion(0)
|
||||
} else {
|
||||
messagePacket, _ = nex.NewPacketV1(targetClient, nil)
|
||||
messagePacket.SetVersion(1)
|
||||
}
|
||||
messagePacket.SetSource(0xA1)
|
||||
messagePacket.SetDestination(0xAF)
|
||||
messagePacket.SetType(nex.DataPacket)
|
||||
messagePacket.SetPayload(rmcMessageBytes)
|
||||
|
||||
messagePacket.AddFlag(nex.FlagNeedsAck)
|
||||
messagePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(messagePacket)
|
||||
|
||||
return 0
|
||||
return rmcResponse, nil
|
||||
}
|
||||
|
|
69
matchmake-extension/browse_matchmake_session.go
Normal file
69
matchmake-extension/browse_matchmake_session.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) browseMatchmakeSession(err error, packet nex.PacketInterface, callID uint32, searchCriteria match_making_types.MatchmakeSessionSearchCriteria, resultRange types.ResultRange) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
searchCriterias := []match_making_types.MatchmakeSessionSearchCriteria{searchCriteria}
|
||||
|
||||
lstSearchCriteria := types.NewList[match_making_types.MatchmakeSessionSearchCriteria]()
|
||||
lstSearchCriteria = searchCriterias
|
||||
|
||||
if commonProtocol.CleanupMatchmakeSessionSearchCriterias != nil {
|
||||
commonProtocol.CleanupMatchmakeSessionSearchCriterias(lstSearchCriteria)
|
||||
}
|
||||
|
||||
sessions, nexError := database.FindMatchmakeSessionBySearchCriteria(commonProtocol.manager, connection, searchCriterias, resultRange, nil)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
lstGathering := types.NewList[match_making_types.GatheringHolder]()
|
||||
|
||||
for _, session := range sessions {
|
||||
// * Scrap session key and user password
|
||||
session.SessionKey = make([]byte, 0)
|
||||
session.UserPassword = ""
|
||||
|
||||
matchmakeSessionDataHolder := match_making_types.NewGatheringHolder()
|
||||
matchmakeSessionDataHolder.Object = session.Copy().(match_making_types.GatheringInterface)
|
||||
|
||||
lstGathering = append(lstGathering, matchmakeSessionDataHolder)
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
lstGathering.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodBrowseMatchmakeSession
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterBrowseMatchmakeSession != nil {
|
||||
go commonProtocol.OnAfterBrowseMatchmakeSession(packet, searchCriteria, resultRange)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
51
matchmake-extension/close_participation.go
Normal file
51
matchmake-extension/close_participation.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) closeParticipation(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
session, _, nexError := database.GetMatchmakeSessionByID(commonProtocol.manager, endpoint, uint32(gid))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
if !session.Gathering.OwnerPID.Equals(connection.PID()) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
nexError = database.UpdateParticipation(commonProtocol.manager, uint32(gid), false)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodCloseParticipation
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterCloseParticipation != nil {
|
||||
go commonProtocol.OnAfterCloseParticipation(packet, gid)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
69
matchmake-extension/create_community.go
Normal file
69
matchmake-extension/create_community.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_database "github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) createCommunity(err error, packet nex.PacketInterface, callID uint32, community match_making_types.PersistentGathering, strMessage types.String) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
if !common_globals.CheckValidPersistentGathering(community) {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
createdPersistentGatherings, nexError := database.GetCreatedPersistentGatherings(commonProtocol.manager, connection.PID())
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
if createdPersistentGatherings >= commonProtocol.PersistentGatheringCreationMax {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PersistentGatheringCreationMax, "change_error")
|
||||
}
|
||||
|
||||
nexError = database.CreatePersistentGathering(commonProtocol.manager, connection, &community)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
_, nexError = match_making_database.JoinGathering(commonProtocol.manager, uint32(community.ID), connection, 1, string(strMessage))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
community.ID.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodCreateCommunity
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterCreateCommunity != nil {
|
||||
go commonProtocol.OnAfterCreateCommunity(packet, community, strMessage)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
|
@ -1,95 +1,85 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func createMatchmakeSession(err error, client *nex.Client, callID uint32, anyGathering *nex.DataHolder, message string, participationCount uint16) uint32 {
|
||||
func (commonProtocol *CommonProtocol) createMatchmakeSession(err error, packet nex.PacketInterface, callID uint32, anyGathering match_making_types.GatheringHolder, message types.String, participationCount types.UInt16) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
server := client.Server()
|
||||
if len(message) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
// A client may disconnect from a session without leaving reliably,
|
||||
// so let's make sure the client is removed from the session
|
||||
common_globals.RemoveConnectionIDFromAllSessions(client.ConnectionID())
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
server := endpoint.Server
|
||||
|
||||
var matchmakeSession *match_making_types.MatchmakeSession
|
||||
anyGatheringDataType := anyGathering.TypeName()
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
if anyGatheringDataType == "MatchmakeSession" {
|
||||
matchmakeSession = anyGathering.ObjectData().(*match_making_types.MatchmakeSession)
|
||||
// * A client may disconnect from a session without leaving reliably,
|
||||
// * so let's make sure the client is removed from the session
|
||||
database.EndMatchmakeSessionsParticipation(commonProtocol.manager, connection)
|
||||
|
||||
var matchmakeSession match_making_types.MatchmakeSession
|
||||
|
||||
if anyGathering.Object.GatheringObjectID().Equals(types.NewString("MatchmakeSession")) {
|
||||
matchmakeSession = anyGathering.Object.(match_making_types.MatchmakeSession)
|
||||
} else {
|
||||
logger.Critical("Non-MatchmakeSession DataType?!")
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Critical("Non-MatchmakeSession DataType?!")
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
sessionIndex := common_globals.GetSessionIndex()
|
||||
// This should in theory be impossible, as there aren't enough PIDs creating sessions to fill the uint32 limit.
|
||||
// If we ever get here, we must be not deleting sessions properly
|
||||
if sessionIndex == 0 {
|
||||
logger.Critical("No gatherings available!")
|
||||
return nex.Errors.RendezVous.LimitExceeded
|
||||
if !common_globals.CheckValidMatchmakeSession(matchmakeSession) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
session := common_globals.CommonMatchmakeSession{
|
||||
SearchCriteria: make([]*match_making_types.MatchmakeSessionSearchCriteria, 0),
|
||||
GameMatchmakeSession: matchmakeSession,
|
||||
nexError := database.CreateMatchmakeSession(commonProtocol.manager, connection, &matchmakeSession)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
common_globals.Sessions[sessionIndex] = &session
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.ID = sessionIndex
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.OwnerPID = client.PID()
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.Gathering.HostPID = client.PID()
|
||||
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.StartedTime = nex.NewDateTime(0)
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.StartedTime.UTC()
|
||||
common_globals.Sessions[sessionIndex].GameMatchmakeSession.SessionKey = make([]byte, 32)
|
||||
|
||||
err, errCode := common_globals.AddPlayersToSession(common_globals.Sessions[sessionIndex], []uint32{client.ConnectionID()})
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return errCode
|
||||
participants, nexError := database.JoinMatchmakeSession(commonProtocol.manager, matchmakeSession, connection, uint16(participationCount), string(message))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(server)
|
||||
matchmakeSession.ParticipationCount = types.NewUInt32(participants)
|
||||
|
||||
rmcResponseStream.WriteUInt32LE(sessionIndex)
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
if server.MatchMakingProtocolVersion().Major <= 3 {
|
||||
rmcResponseStream.WriteBuffer(matchmakeSession.SessionKey)
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
matchmakeSession.Gathering.ID.WriteTo(rmcResponseStream)
|
||||
|
||||
if server.LibraryVersions.MatchMaking.GreaterOrEqual("3.0.0") {
|
||||
matchmakeSession.SessionKey.WriteTo(rmcResponseStream)
|
||||
}
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(matchmake_extension.MethodCreateMatchmakeSession, rmcResponseBody)
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodCreateMatchmakeSession
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
var responsePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
responsePacket, _ = nex.NewPacketV0(client, nil)
|
||||
responsePacket.SetVersion(0)
|
||||
} else {
|
||||
responsePacket, _ = nex.NewPacketV1(client, nil)
|
||||
responsePacket.SetVersion(1)
|
||||
if commonProtocol.OnAfterCreateMatchmakeSession != nil {
|
||||
go commonProtocol.OnAfterCreateMatchmakeSession(packet, anyGathering, message, participationCount)
|
||||
}
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
return rmcResponse, nil
|
||||
}
|
||||
|
|
|
@ -1,137 +1,80 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/notifications/types"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/v2/match-making/constants"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func createMatchmakeSessionWithParam(err error, client *nex.Client, callID uint32, createMatchmakeSessionParam *match_making_types.CreateMatchmakeSessionParam) uint32 {
|
||||
func (commonProtocol *CommonProtocol) createMatchmakeSessionWithParam(err error, packet nex.PacketInterface, callID uint32, createMatchmakeSessionParam match_making_types.CreateMatchmakeSessionParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
server := client.Server()
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
if !common_globals.CheckValidMatchmakeSession(createMatchmakeSessionParam.SourceMatchmakeSession) {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
if len(createMatchmakeSessionParam.JoinMessage) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
if createMatchmakeSessionParam.GIDForParticipationCheck != 0 {
|
||||
// * Check that all new participants are participating in the specified gathering ID
|
||||
nexError := database.CheckGatheringForParticipation(commonProtocol.manager, uint32(createMatchmakeSessionParam.GIDForParticipationCheck), append(createMatchmakeSessionParam.AdditionalParticipants, connection.PID()))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
}
|
||||
|
||||
// * A client may disconnect from a session without leaving reliably,
|
||||
// * so let's make sure the client is removed from all sessions
|
||||
common_globals.RemoveConnectionIDFromAllSessions(client.ConnectionID())
|
||||
database.EndMatchmakeSessionsParticipation(commonProtocol.manager, connection)
|
||||
|
||||
joinedMatchmakeSession := createMatchmakeSessionParam.SourceMatchmakeSession.Copy().(*match_making_types.MatchmakeSession)
|
||||
|
||||
sessionIndex := common_globals.GetSessionIndex()
|
||||
if sessionIndex == 0 {
|
||||
logger.Critical("No gatherings available!")
|
||||
return nex.Errors.RendezVous.LimitExceeded
|
||||
joinedMatchmakeSession := createMatchmakeSessionParam.SourceMatchmakeSession.Copy().(match_making_types.MatchmakeSession)
|
||||
nexError := database.CreateMatchmakeSession(commonProtocol.manager, connection, &joinedMatchmakeSession)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
joinedMatchmakeSession.SetStructureVersion(3)
|
||||
joinedMatchmakeSession.Gathering.ID = sessionIndex
|
||||
joinedMatchmakeSession.Gathering.OwnerPID = client.PID()
|
||||
joinedMatchmakeSession.Gathering.HostPID = client.PID()
|
||||
joinedMatchmakeSession.StartedTime = nex.NewDateTime(0)
|
||||
joinedMatchmakeSession.StartedTime.UTC()
|
||||
joinedMatchmakeSession.SessionKey = make([]byte, 32)
|
||||
|
||||
// TODO - Are these parameters game-specific?
|
||||
|
||||
joinedMatchmakeSession.MatchmakeParam.Parameters["@SR"] = nex.NewVariant()
|
||||
joinedMatchmakeSession.MatchmakeParam.Parameters["@SR"].TypeID = 3
|
||||
joinedMatchmakeSession.MatchmakeParam.Parameters["@SR"].Bool = true
|
||||
|
||||
joinedMatchmakeSession.MatchmakeParam.Parameters["@GIR"] = nex.NewVariant()
|
||||
joinedMatchmakeSession.MatchmakeParam.Parameters["@GIR"].TypeID = 1
|
||||
joinedMatchmakeSession.MatchmakeParam.Parameters["@GIR"].Int64 = 3
|
||||
|
||||
common_globals.Sessions[sessionIndex] = &common_globals.CommonMatchmakeSession{
|
||||
SearchCriteria: make([]*match_making_types.MatchmakeSessionSearchCriteria, 0),
|
||||
GameMatchmakeSession: joinedMatchmakeSession,
|
||||
participants, nexError := database.JoinMatchmakeSessionWithParticipants(commonProtocol.manager, joinedMatchmakeSession, connection, createMatchmakeSessionParam.AdditionalParticipants, string(createMatchmakeSessionParam.JoinMessage), constants.JoinMatchmakeSessionBehaviorJoinMyself)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
err, errCode := common_globals.AddPlayersToSession(common_globals.Sessions[sessionIndex], []uint32{client.ConnectionID()})
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return errCode
|
||||
}
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(server)
|
||||
joinedMatchmakeSession.ParticipationCount = types.NewUInt32(participants)
|
||||
|
||||
rmcResponseStream.WriteStructure(joinedMatchmakeSession)
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
joinedMatchmakeSession.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(matchmake_extension.MethodCreateMatchmakeSessionWithParam, rmcResponseBody)
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodCreateMatchmakeSessionWithParam
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
var responsePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
responsePacket, _ = nex.NewPacketV0(client, nil)
|
||||
responsePacket.SetVersion(0)
|
||||
} else {
|
||||
responsePacket, _ = nex.NewPacketV1(client, nil)
|
||||
responsePacket.SetVersion(1)
|
||||
if commonProtocol.OnAfterCreateMatchmakeSessionWithParam != nil {
|
||||
go commonProtocol.OnAfterCreateMatchmakeSessionWithParam(packet, createMatchmakeSessionParam)
|
||||
}
|
||||
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(responsePacket)
|
||||
|
||||
// * Works for Minecraft, not tried on anything else
|
||||
notificationRequestMessage := nex.NewRMCRequest()
|
||||
notificationRequestMessage.SetProtocolID(notifications.ProtocolID)
|
||||
notificationRequestMessage.SetCallID(0xffff0000 + callID)
|
||||
notificationRequestMessage.SetMethodID(notifications.MethodProcessNotificationEvent)
|
||||
|
||||
notificationCategory := notifications.NotificationCategories.Participation
|
||||
notificationSubtype := notifications.NotificationSubTypes.Participation.NewParticipant
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = client.PID()
|
||||
oEvent.Type = notifications.BuildNotificationType(notificationCategory, notificationSubtype)
|
||||
oEvent.Param1 = sessionIndex
|
||||
oEvent.Param2 = client.PID()
|
||||
oEvent.StrParam = ""
|
||||
oEvent.Param3 = 1
|
||||
|
||||
notificationStream := nex.NewStreamOut(server)
|
||||
|
||||
notificationStream.WriteStructure(oEvent)
|
||||
|
||||
notificationRequestMessage.SetParameters(notificationStream.Bytes())
|
||||
notificationRequestBytes := notificationRequestMessage.Bytes()
|
||||
|
||||
var messagePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
messagePacket, _ = nex.NewPacketV0(client, nil)
|
||||
messagePacket.SetVersion(0)
|
||||
} else {
|
||||
messagePacket, _ = nex.NewPacketV1(client, nil)
|
||||
messagePacket.SetVersion(1)
|
||||
}
|
||||
|
||||
messagePacket.SetSource(0xA1)
|
||||
messagePacket.SetDestination(0xAF)
|
||||
messagePacket.SetType(nex.DataPacket)
|
||||
messagePacket.SetPayload(notificationRequestBytes)
|
||||
|
||||
messagePacket.AddFlag(nex.FlagNeedsAck)
|
||||
messagePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(messagePacket)
|
||||
|
||||
return 0
|
||||
return rmcResponse, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
)
|
||||
|
||||
// CheckGatheringForParticipation checks that the given PIDs are participating on the gathering ID
|
||||
func CheckGatheringForParticipation(manager *common_globals.MatchmakingManager, gatheringID uint32, participantsCheck []types.PID) *nex.Error {
|
||||
_, _, participants, _, err := database.FindGatheringByID(manager, gatheringID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, participant := range participantsCheck {
|
||||
if !slices.Contains(participants, uint64(participant)) {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.NotParticipatedGathering, "change_error")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
100
matchmake-extension/database/create_matchmake_session.go
Normal file
100
matchmake-extension/database/create_matchmake_session.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_database "github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// CreateMatchmakeSession creates a new MatchmakeSession on the database. No participants are added
|
||||
func CreateMatchmakeSession(manager *common_globals.MatchmakingManager, connection *nex.PRUDPConnection, matchmakeSession *match_making_types.MatchmakeSession) *nex.Error {
|
||||
startedTime, nexError := match_making_database.RegisterGathering(manager, connection.PID(), connection.PID(), &matchmakeSession.Gathering, "MatchmakeSession")
|
||||
if nexError != nil {
|
||||
return nexError
|
||||
}
|
||||
|
||||
attribs := make([]uint32, len(matchmakeSession.Attributes))
|
||||
for i, value := range matchmakeSession.Attributes {
|
||||
attribs[i] = uint32(value)
|
||||
}
|
||||
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
matchmakeParam := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
srVariant := types.NewVariant()
|
||||
srVariant.TypeID = 3
|
||||
srVariant.Type = types.NewBool(true)
|
||||
matchmakeSession.MatchmakeParam.Params["@SR"] = srVariant
|
||||
girVariant := types.NewVariant()
|
||||
girVariant.TypeID = 1
|
||||
girVariant.Type = types.NewInt64(3)
|
||||
matchmakeSession.MatchmakeParam.Params["@GIR"] = girVariant
|
||||
|
||||
matchmakeSession.MatchmakeParam.WriteTo(matchmakeParam)
|
||||
|
||||
matchmakeSession.StartedTime = startedTime
|
||||
matchmakeSession.SessionKey = make([]byte, 32)
|
||||
matchmakeSession.SystemPasswordEnabled = false
|
||||
rand.Read(matchmakeSession.SessionKey)
|
||||
|
||||
_, err := manager.Database.Exec(`INSERT INTO matchmaking.matchmake_sessions (
|
||||
id,
|
||||
game_mode,
|
||||
attribs,
|
||||
open_participation,
|
||||
matchmake_system_type,
|
||||
application_buffer,
|
||||
progress_score,
|
||||
session_key,
|
||||
option_zero,
|
||||
matchmake_param,
|
||||
user_password,
|
||||
refer_gid,
|
||||
user_password_enabled,
|
||||
system_password_enabled,
|
||||
codeword
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9,
|
||||
$10,
|
||||
$11,
|
||||
$12,
|
||||
$13,
|
||||
$14,
|
||||
$15
|
||||
)`,
|
||||
matchmakeSession.Gathering.ID,
|
||||
matchmakeSession.GameMode,
|
||||
pqextended.Array(attribs),
|
||||
matchmakeSession.OpenParticipation,
|
||||
matchmakeSession.MatchmakeSystemType,
|
||||
matchmakeSession.ApplicationBuffer,
|
||||
matchmakeSession.ProgressScore,
|
||||
matchmakeSession.SessionKey,
|
||||
matchmakeSession.Option,
|
||||
matchmakeParam.Bytes(),
|
||||
matchmakeSession.UserPassword,
|
||||
matchmakeSession.ReferGID,
|
||||
matchmakeSession.UserPasswordEnabled,
|
||||
matchmakeSession.SystemPasswordEnabled,
|
||||
matchmakeSession.CodeWord,
|
||||
)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
54
matchmake-extension/database/create_persistent_gathering.go
Normal file
54
matchmake-extension/database/create_persistent_gathering.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_database "github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// CreatePersistentGathering creates a new PersistentGathering on the database. No participants are added
|
||||
func CreatePersistentGathering(manager *common_globals.MatchmakingManager, connection *nex.PRUDPConnection, persistentGathering *match_making_types.PersistentGathering) *nex.Error {
|
||||
_, nexError := match_making_database.RegisterGathering(manager, connection.PID(), types.NewPID(0), &persistentGathering.Gathering, "PersistentGathering")
|
||||
if nexError != nil {
|
||||
return nexError
|
||||
}
|
||||
|
||||
attribs := make([]uint32, len(persistentGathering.Attribs))
|
||||
for i, value := range persistentGathering.Attribs {
|
||||
attribs[i] = uint32(value)
|
||||
}
|
||||
|
||||
_, err := manager.Database.Exec(`INSERT INTO matchmaking.persistent_gatherings (
|
||||
id,
|
||||
community_type,
|
||||
password,
|
||||
attribs,
|
||||
application_buffer,
|
||||
participation_start_date,
|
||||
participation_end_date
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
)`,
|
||||
uint32(persistentGathering.Gathering.ID),
|
||||
uint32(persistentGathering.CommunityType),
|
||||
string(persistentGathering.Password),
|
||||
pqextended.Array(attribs),
|
||||
[]byte(persistentGathering.ApplicationBuffer),
|
||||
persistentGathering.ParticipationStartDate.Standard(),
|
||||
persistentGathering.ParticipationEndDate.Standard(),
|
||||
)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
)
|
||||
|
||||
// EndMatchmakeSessionsParticipation ends participation on all matchmake sessions
|
||||
func EndMatchmakeSessionsParticipation(manager *common_globals.MatchmakingManager, connection *nex.PRUDPConnection) {
|
||||
rows, err := manager.Database.Query(`SELECT id FROM matchmaking.gatherings WHERE type='MatchmakeSession' AND $1=ANY(participants)`, connection.PID())
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var gatheringID uint32
|
||||
err = rows.Scan(&gatheringID)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
database.EndGatheringParticipation(manager, gatheringID, connection, "")
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
145
matchmake-extension/database/find_matchmake_session.go
Normal file
145
matchmake-extension/database/find_matchmake_session.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// FindMatchmakeSession finds a matchmake session with the given search matchmake session
|
||||
func FindMatchmakeSession(manager *common_globals.MatchmakingManager, connection *nex.PRUDPConnection, searchMatchmakeSession match_making_types.MatchmakeSession) (*match_making_types.MatchmakeSession, *nex.Error) {
|
||||
attribs := make([]uint32, len(searchMatchmakeSession.Attributes))
|
||||
for i, value := range searchMatchmakeSession.Attributes {
|
||||
attribs[i] = uint32(value)
|
||||
}
|
||||
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
searchStatement := `SELECT
|
||||
g.id,
|
||||
g.owner_pid,
|
||||
g.host_pid,
|
||||
g.min_participants,
|
||||
g.max_participants,
|
||||
g.participation_policy,
|
||||
g.policy_argument,
|
||||
g.flags,
|
||||
g.state,
|
||||
g.description,
|
||||
array_length(g.participants, 1),
|
||||
g.started_time,
|
||||
ms.game_mode,
|
||||
ms.attribs,
|
||||
ms.open_participation,
|
||||
ms.matchmake_system_type,
|
||||
ms.application_buffer,
|
||||
ms.progress_score,
|
||||
ms.session_key,
|
||||
ms.option_zero,
|
||||
ms.matchmake_param,
|
||||
ms.user_password,
|
||||
ms.refer_gid,
|
||||
ms.user_password_enabled,
|
||||
ms.system_password_enabled,
|
||||
ms.codeword
|
||||
FROM matchmaking.gatherings AS g
|
||||
INNER JOIN matchmaking.matchmake_sessions AS ms ON ms.id = g.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='MatchmakeSession' AND
|
||||
g.host_pid <> 0 AND
|
||||
ms.open_participation=true AND
|
||||
array_length(g.participants, 1) < g.max_participants AND
|
||||
ms.user_password_enabled=false AND
|
||||
ms.system_password_enabled=false AND
|
||||
g.max_participants=$1 AND
|
||||
g.min_participants=$2 AND
|
||||
ms.game_mode=$3 AND
|
||||
ms.attribs[1]=$4 AND
|
||||
ms.attribs[3]=$6 AND
|
||||
ms.attribs[4]=$7 AND
|
||||
ms.attribs[5]=$8 AND
|
||||
ms.attribs[6]=$9 AND
|
||||
ms.matchmake_system_type=$10 AND
|
||||
ms.codeword=$11 AND (CASE WHEN g.participation_policy=98 THEN g.owner_pid=ANY($12) ELSE true END)
|
||||
ORDER BY abs($5 - ms.attribs[2])` // * Use "Closest attribute" selection method, guessing from Mario Kart 7
|
||||
|
||||
var friendList []uint32
|
||||
// * Prevent access to friend rooms if not implemented
|
||||
if manager.GetUserFriendPIDs != nil {
|
||||
friendList = manager.GetUserFriendPIDs(uint32(connection.PID()))
|
||||
}
|
||||
|
||||
resultMatchmakeSession := match_making_types.NewMatchmakeSession()
|
||||
var startedTime time.Time
|
||||
var resultAttribs []uint32
|
||||
var resultMatchmakeParam []byte
|
||||
|
||||
// * For simplicity, we will only compare the values that exist on a MatchmakeSessionSearchCriteria
|
||||
err := manager.Database.QueryRow(searchStatement,
|
||||
searchMatchmakeSession.Gathering.MaximumParticipants,
|
||||
searchMatchmakeSession.Gathering.MinimumParticipants,
|
||||
searchMatchmakeSession.GameMode,
|
||||
attribs[0],
|
||||
attribs[1],
|
||||
attribs[2],
|
||||
attribs[3],
|
||||
attribs[4],
|
||||
attribs[5],
|
||||
searchMatchmakeSession.MatchmakeSystemType,
|
||||
searchMatchmakeSession.CodeWord,
|
||||
pqextended.Array(friendList),
|
||||
).Scan(
|
||||
&resultMatchmakeSession.Gathering.ID,
|
||||
&resultMatchmakeSession.Gathering.OwnerPID,
|
||||
&resultMatchmakeSession.Gathering.HostPID,
|
||||
&resultMatchmakeSession.Gathering.MinimumParticipants,
|
||||
&resultMatchmakeSession.Gathering.MaximumParticipants,
|
||||
&resultMatchmakeSession.Gathering.ParticipationPolicy,
|
||||
&resultMatchmakeSession.Gathering.PolicyArgument,
|
||||
&resultMatchmakeSession.Gathering.Flags,
|
||||
&resultMatchmakeSession.Gathering.State,
|
||||
&resultMatchmakeSession.Gathering.Description,
|
||||
&resultMatchmakeSession.ParticipationCount,
|
||||
&startedTime,
|
||||
&resultMatchmakeSession.GameMode,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultMatchmakeSession.OpenParticipation,
|
||||
&resultMatchmakeSession.MatchmakeSystemType,
|
||||
&resultMatchmakeSession.ApplicationBuffer,
|
||||
&resultMatchmakeSession.ProgressScore,
|
||||
&resultMatchmakeSession.SessionKey,
|
||||
&resultMatchmakeSession.Option,
|
||||
&resultMatchmakeParam,
|
||||
&resultMatchmakeSession.UserPassword,
|
||||
&resultMatchmakeSession.ReferGID,
|
||||
&resultMatchmakeSession.UserPasswordEnabled,
|
||||
&resultMatchmakeSession.SystemPasswordEnabled,
|
||||
&resultMatchmakeSession.CodeWord,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
} else {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
resultMatchmakeSession.StartedTime = resultMatchmakeSession.StartedTime.FromTimestamp(startedTime)
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultMatchmakeSession.Attributes = attributesSlice
|
||||
|
||||
matchmakeParamBytes := nex.NewByteStreamIn(resultMatchmakeParam, endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
resultMatchmakeSession.MatchmakeParam.ExtractFrom(matchmakeParamBytes)
|
||||
|
||||
return &resultMatchmakeSession, nil
|
||||
}
|
|
@ -0,0 +1,360 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/v2/match-making/constants"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// FindMatchmakeSessionBySearchCriteria finds matchmake sessions with the given search criterias
|
||||
func FindMatchmakeSessionBySearchCriteria(manager *common_globals.MatchmakingManager, connection *nex.PRUDPConnection, searchCriterias []match_making_types.MatchmakeSessionSearchCriteria, resultRange types.ResultRange, sourceMatchmakeSession *match_making_types.MatchmakeSession) ([]match_making_types.MatchmakeSession, *nex.Error) {
|
||||
resultMatchmakeSessions := make([]match_making_types.MatchmakeSession, 0)
|
||||
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
var friendList []uint32
|
||||
if manager.GetUserFriendPIDs != nil {
|
||||
friendList = manager.GetUserFriendPIDs(uint32(connection.PID()))
|
||||
}
|
||||
|
||||
if resultRange.Offset == math.MaxUint32 {
|
||||
resultRange.Offset = 0
|
||||
}
|
||||
|
||||
for _, searchCriteria := range searchCriterias {
|
||||
searchStatement := `SELECT
|
||||
g.id,
|
||||
g.owner_pid,
|
||||
g.host_pid,
|
||||
g.min_participants,
|
||||
g.max_participants,
|
||||
g.participation_policy,
|
||||
g.policy_argument,
|
||||
g.flags,
|
||||
g.state,
|
||||
g.description,
|
||||
array_length(g.participants, 1),
|
||||
g.started_time,
|
||||
ms.game_mode,
|
||||
ms.attribs,
|
||||
ms.open_participation,
|
||||
ms.matchmake_system_type,
|
||||
ms.application_buffer,
|
||||
ms.progress_score,
|
||||
ms.session_key,
|
||||
ms.option_zero,
|
||||
ms.matchmake_param,
|
||||
ms.user_password,
|
||||
ms.refer_gid,
|
||||
ms.user_password_enabled,
|
||||
ms.system_password_enabled,
|
||||
ms.codeword
|
||||
FROM matchmaking.gatherings AS g
|
||||
INNER JOIN matchmaking.matchmake_sessions AS ms ON ms.id = g.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='MatchmakeSession' AND
|
||||
ms.refer_gid=$1 AND
|
||||
ms.codeword=$2 AND
|
||||
array_length(ms.attribs, 1)=$3 AND
|
||||
(CASE WHEN g.participation_policy=98 THEN g.owner_pid=ANY($4) ELSE true END) AND
|
||||
(CASE WHEN $5=true THEN ms.open_participation=true ELSE true END) AND
|
||||
(CASE WHEN $6=true THEN g.host_pid <> 0 ELSE true END) AND
|
||||
(CASE WHEN $7=true THEN ms.user_password_enabled=false ELSE true END) AND
|
||||
(CASE WHEN $8=true THEN ms.system_password_enabled=false ELSE true END)`
|
||||
|
||||
var valid bool = true
|
||||
for i, attrib := range searchCriteria.Attribs {
|
||||
// * Ignore attribute 1 here, reserved for the selection method
|
||||
if i == 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if attrib != "" {
|
||||
before, after, found := strings.Cut(string(attrib), ",")
|
||||
if found {
|
||||
min, err := strconv.ParseUint(before, 10, 32)
|
||||
if err != nil {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
|
||||
max, err := strconv.ParseUint(after, 10, 32)
|
||||
if err != nil {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND ms.attribs[%d] BETWEEN %d AND %d`, i + 1, min, max)
|
||||
} else {
|
||||
value, err := strconv.ParseUint(before, 10, 32)
|
||||
if err != nil {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND ms.attribs[%d]=%d`, i + 1, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// * Search criteria is invalid, continue to next one
|
||||
if !valid {
|
||||
continue
|
||||
}
|
||||
|
||||
if searchCriteria.MaxParticipants != "" {
|
||||
before, after, found := strings.Cut(string(searchCriteria.MaxParticipants), ",")
|
||||
if found {
|
||||
min, err := strconv.ParseUint(before, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
max, err := strconv.ParseUint(after, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND g.max_participants BETWEEN %d AND %d`, min, max)
|
||||
} else {
|
||||
value, err := strconv.ParseUint(before, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND g.max_participants=%d`, value)
|
||||
}
|
||||
}
|
||||
|
||||
if searchCriteria.MinParticipants != "" {
|
||||
before, after, found := strings.Cut(string(searchCriteria.MinParticipants), ",")
|
||||
if found {
|
||||
min, err := strconv.ParseUint(before, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
max, err := strconv.ParseUint(after, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND g.min_participants BETWEEN %d AND %d`, min, max)
|
||||
} else {
|
||||
value, err := strconv.ParseUint(before, 10, 16)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND g.min_participants=%d`, value)
|
||||
}
|
||||
}
|
||||
|
||||
if searchCriteria.GameMode != "" {
|
||||
before, after, found := strings.Cut(string(searchCriteria.GameMode), ",")
|
||||
if found {
|
||||
min, err := strconv.ParseUint(before, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
max, err := strconv.ParseUint(after, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND ms.game_mode BETWEEN %d AND %d`, min, max)
|
||||
} else {
|
||||
value, err := strconv.ParseUint(before, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND ms.game_mode=%d`, value)
|
||||
}
|
||||
}
|
||||
|
||||
if searchCriteria.MatchmakeSystemType != "" {
|
||||
before, after, found := strings.Cut(string(searchCriteria.MatchmakeSystemType), ",")
|
||||
if found {
|
||||
min, err := strconv.ParseUint(before, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
max, err := strconv.ParseUint(after, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND ms.matchmake_system_type BETWEEN %d AND %d`, min, max)
|
||||
} else {
|
||||
value, err := strconv.ParseUint(before, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` AND ms.matchmake_system_type=%d`, value)
|
||||
}
|
||||
}
|
||||
|
||||
// * Filter full sessions if necessary
|
||||
if searchCriteria.VacantOnly {
|
||||
// * Account for the VacantParticipants when searching for sessions (if given)
|
||||
if searchCriteria.VacantParticipants == 0 {
|
||||
searchStatement += ` AND array_length(g.participants, 1) + 1 <= g.max_participants`
|
||||
} else {
|
||||
searchStatement += fmt.Sprintf(` AND array_length(g.participants, 1) + %d <= g.max_participants`, searchCriteria.VacantParticipants)
|
||||
}
|
||||
}
|
||||
|
||||
switch constants.SelectionMethod(searchCriteria.SelectionMethod) {
|
||||
case constants.SelectionMethodRandom:
|
||||
// * Random global
|
||||
searchStatement += ` ORDER BY RANDOM()`
|
||||
case constants.SelectionMethodNearestNeighbor:
|
||||
// * Closest attribute
|
||||
attribute1, err := strconv.ParseUint(string(searchCriteria.Attribs[1]), 10, 32)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` ORDER BY abs(%d - ms.attribs[2])`, attribute1)
|
||||
case constants.SelectionMethodBroadenRange:
|
||||
// * Ranked
|
||||
|
||||
// TODO - Actually implement ranked matchmaking, using closest attribute at the moment
|
||||
attribute1, err := strconv.ParseUint(string(searchCriteria.Attribs[1]), 10, 32)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` ORDER BY abs(%d - ms.attribs[2])`, attribute1)
|
||||
case constants.SelectionMethodProgressScore:
|
||||
// * Progress Score
|
||||
|
||||
// * We can only use this when doing auto-matchmake
|
||||
if sourceMatchmakeSession == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchStatement += fmt.Sprintf(` ORDER BY abs(%d - ms.progress_score)`, sourceMatchmakeSession.ProgressScore)
|
||||
case constants.SelectionMethodBroadenRangeWithProgressScore:
|
||||
// * Ranked + Progress
|
||||
|
||||
// TODO - Actually implement ranked matchmaking, using closest attribute at the moment
|
||||
|
||||
// * We can only use this when doing auto-matchmake
|
||||
if sourceMatchmakeSession == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if searchCriteria.Attribs[1] != "" {
|
||||
attribute1, err := strconv.ParseUint(string(searchCriteria.Attribs[1]), 10, 32)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO - Should the attribute and the progress score actually weigh the same?
|
||||
searchStatement += fmt.Sprintf(` ORDER BY abs(%d - ms.attribs[2] + %d - ms.progress_score)`, attribute1, sourceMatchmakeSession.ProgressScore)
|
||||
}
|
||||
|
||||
// case constants.SelectionMethodScoreBased: // * According to notes this is related with the MatchmakeParam. TODO - Implement this
|
||||
}
|
||||
|
||||
// * If the ResultRange inside the MatchmakeSessionSearchCriteria is valid (only present on NEX 4.0+), use that
|
||||
// * Otherwise, use the one given as argument
|
||||
if searchCriteria.ResultRange.Length != 0 {
|
||||
searchStatement += fmt.Sprintf(` LIMIT %d OFFSET %d`, uint32(searchCriteria.ResultRange.Length), uint32(searchCriteria.ResultRange.Offset))
|
||||
} else {
|
||||
// * Since we use one ResultRange for all searches, limit the total length to the one specified
|
||||
// * but apply the same offset to all queries
|
||||
searchStatement += fmt.Sprintf(` LIMIT %d OFFSET %d`, uint32(resultRange.Length) - uint32(len(resultMatchmakeSessions)), uint32(resultRange.Offset))
|
||||
}
|
||||
|
||||
rows, err := manager.Database.Query(searchStatement,
|
||||
searchCriteria.ReferGID,
|
||||
searchCriteria.CodeWord,
|
||||
len(searchCriteria.Attribs),
|
||||
pqextended.Array(friendList),
|
||||
searchCriteria.ExcludeLocked,
|
||||
searchCriteria.ExcludeNonHostPID,
|
||||
searchCriteria.ExcludeUserPasswordSet,
|
||||
searchCriteria.ExcludeSystemPasswordSet,
|
||||
)
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
resultMatchmakeSession := match_making_types.NewMatchmakeSession()
|
||||
var startedTime time.Time
|
||||
var resultAttribs []uint32
|
||||
var resultMatchmakeParam []byte
|
||||
|
||||
err = rows.Scan(
|
||||
&resultMatchmakeSession.Gathering.ID,
|
||||
&resultMatchmakeSession.Gathering.OwnerPID,
|
||||
&resultMatchmakeSession.Gathering.HostPID,
|
||||
&resultMatchmakeSession.Gathering.MinimumParticipants,
|
||||
&resultMatchmakeSession.Gathering.MaximumParticipants,
|
||||
&resultMatchmakeSession.Gathering.ParticipationPolicy,
|
||||
&resultMatchmakeSession.Gathering.PolicyArgument,
|
||||
&resultMatchmakeSession.Gathering.Flags,
|
||||
&resultMatchmakeSession.Gathering.State,
|
||||
&resultMatchmakeSession.Gathering.Description,
|
||||
&resultMatchmakeSession.ParticipationCount,
|
||||
&startedTime,
|
||||
&resultMatchmakeSession.GameMode,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultMatchmakeSession.OpenParticipation,
|
||||
&resultMatchmakeSession.MatchmakeSystemType,
|
||||
&resultMatchmakeSession.ApplicationBuffer,
|
||||
&resultMatchmakeSession.ProgressScore,
|
||||
&resultMatchmakeSession.SessionKey,
|
||||
&resultMatchmakeSession.Option,
|
||||
&resultMatchmakeParam,
|
||||
&resultMatchmakeSession.UserPassword,
|
||||
&resultMatchmakeSession.ReferGID,
|
||||
&resultMatchmakeSession.UserPasswordEnabled,
|
||||
&resultMatchmakeSession.SystemPasswordEnabled,
|
||||
&resultMatchmakeSession.CodeWord,
|
||||
)
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
resultMatchmakeSession.StartedTime = resultMatchmakeSession.StartedTime.FromTimestamp(startedTime)
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultMatchmakeSession.Attributes = attributesSlice
|
||||
|
||||
matchmakeParamBytes := nex.NewByteStreamIn(resultMatchmakeParam, endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
resultMatchmakeSession.MatchmakeParam.ExtractFrom(matchmakeParamBytes)
|
||||
|
||||
resultMatchmakeSessions = append(resultMatchmakeSessions, resultMatchmakeSession)
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
}
|
||||
|
||||
return resultMatchmakeSessions, nil
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// GetCreatedPersistentGatherings returns the number of active persistent gatherings that a given PID owns
|
||||
func GetCreatedPersistentGatherings(manager *common_globals.MatchmakingManager, ownerPID types.PID) (int, *nex.Error) {
|
||||
var createdPersistentGatherings int
|
||||
err := manager.Database.QueryRow(`SELECT
|
||||
COUNT(pg.id)
|
||||
FROM matchmaking.persistent_gatherings AS pg
|
||||
INNER JOIN matchmaking.gatherings AS g ON g.id = pg.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.owner_pid=$1`, ownerPID).Scan(&createdPersistentGatherings)
|
||||
if err != nil {
|
||||
return 0, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return createdPersistentGatherings, nil
|
||||
}
|
47
matchmake-extension/database/get_detailed_gathering_by_id.go
Normal file
47
matchmake-extension/database/get_detailed_gathering_by_id.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_database "github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
)
|
||||
|
||||
// GetDetailedGatheringByID returns a Gathering as an RVType by its gathering ID
|
||||
func GetDetailedGatheringByID(manager *common_globals.MatchmakingManager, sourcePID uint64, gatheringID uint32) (types.RVType, string, *nex.Error) {
|
||||
gathering, gatheringType, participants, startedTime, nexError := match_making_database.FindGatheringByID(manager, gatheringID)
|
||||
if nexError != nil {
|
||||
return nil, "", nexError
|
||||
}
|
||||
|
||||
if gatheringType == "Gathering" {
|
||||
return gathering, gatheringType, nil
|
||||
}
|
||||
|
||||
if gatheringType == "MatchmakeSession" {
|
||||
matchmakeSession, nexError := GetMatchmakeSessionByGathering(manager, manager.Endpoint, gathering, uint32(len(participants)), startedTime)
|
||||
if nexError != nil {
|
||||
return nil, "", nexError
|
||||
}
|
||||
|
||||
// * Scrap session key and user password
|
||||
matchmakeSession.SessionKey = make([]byte, 0)
|
||||
matchmakeSession.UserPassword = ""
|
||||
|
||||
return matchmakeSession, gatheringType, nil
|
||||
}
|
||||
|
||||
if gatheringType == "PersistentGathering" {
|
||||
persistentGathering, nexError := GetPersistentGatheringByGathering(manager, gathering, sourcePID)
|
||||
if nexError != nil {
|
||||
return nil, "", nexError
|
||||
}
|
||||
|
||||
// * Scrap persistent gathering password
|
||||
persistentGathering.Password = ""
|
||||
|
||||
return persistentGathering, gatheringType, nil
|
||||
}
|
||||
|
||||
return nil, "", nex.NewError(nex.ResultCodes.Core.Exception, "change_error")
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetMatchmakeSessionByGathering gets a matchmake session with the given gathering data
|
||||
func GetMatchmakeSessionByGathering(manager *common_globals.MatchmakingManager, endpoint *nex.PRUDPEndPoint, gathering match_making_types.Gathering, participationCount uint32, startedTime types.DateTime) (match_making_types.MatchmakeSession, *nex.Error) {
|
||||
resultMatchmakeSession := match_making_types.NewMatchmakeSession()
|
||||
var resultAttribs []uint32
|
||||
var resultMatchmakeParam []byte
|
||||
|
||||
err := manager.Database.QueryRow(`SELECT
|
||||
game_mode,
|
||||
attribs,
|
||||
open_participation,
|
||||
matchmake_system_type,
|
||||
application_buffer,
|
||||
progress_score,
|
||||
session_key,
|
||||
option_zero,
|
||||
matchmake_param,
|
||||
user_password,
|
||||
refer_gid,
|
||||
user_password_enabled,
|
||||
system_password_enabled,
|
||||
codeword
|
||||
FROM matchmaking.matchmake_sessions WHERE id=$1`,
|
||||
uint32(gathering.ID),
|
||||
).Scan(
|
||||
&resultMatchmakeSession.GameMode,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultMatchmakeSession.OpenParticipation,
|
||||
&resultMatchmakeSession.MatchmakeSystemType,
|
||||
&resultMatchmakeSession.ApplicationBuffer,
|
||||
&resultMatchmakeSession.ProgressScore,
|
||||
&resultMatchmakeSession.SessionKey,
|
||||
&resultMatchmakeSession.Option,
|
||||
&resultMatchmakeParam,
|
||||
&resultMatchmakeSession.UserPassword,
|
||||
&resultMatchmakeSession.ReferGID,
|
||||
&resultMatchmakeSession.UserPasswordEnabled,
|
||||
&resultMatchmakeSession.SystemPasswordEnabled,
|
||||
&resultMatchmakeSession.CodeWord,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return match_making_types.NewMatchmakeSession(), nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
} else {
|
||||
return match_making_types.NewMatchmakeSession(), nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
resultMatchmakeSession.Gathering = gathering
|
||||
resultMatchmakeSession.ParticipationCount = types.NewUInt32(participationCount)
|
||||
resultMatchmakeSession.StartedTime = startedTime
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultMatchmakeSession.Attributes = attributesSlice
|
||||
|
||||
matchmakeParamBytes := nex.NewByteStreamIn(resultMatchmakeParam, endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
resultMatchmakeSession.MatchmakeParam.ExtractFrom(matchmakeParamBytes)
|
||||
|
||||
return resultMatchmakeSession, nil
|
||||
}
|
107
matchmake-extension/database/get_matchmake_session_by_id.go
Normal file
107
matchmake-extension/database/get_matchmake_session_by_id.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetMatchmakeSessionByID gets a matchmake session with the given gathering ID and the system password
|
||||
func GetMatchmakeSessionByID(manager *common_globals.MatchmakingManager, endpoint *nex.PRUDPEndPoint, gatheringID uint32) (match_making_types.MatchmakeSession, string, *nex.Error) {
|
||||
resultMatchmakeSession := match_making_types.NewMatchmakeSession()
|
||||
var startedTime time.Time
|
||||
var resultAttribs []uint32
|
||||
var resultMatchmakeParam []byte
|
||||
var systemPassword string
|
||||
|
||||
// * For simplicity, we will only compare the values that exist on a MatchmakeSessionSearchCriteria
|
||||
err := manager.Database.QueryRow(`SELECT
|
||||
g.id,
|
||||
g.owner_pid,
|
||||
g.host_pid,
|
||||
g.min_participants,
|
||||
g.max_participants,
|
||||
g.participation_policy,
|
||||
g.policy_argument,
|
||||
g.flags,
|
||||
g.state,
|
||||
g.description,
|
||||
array_length(g.participants, 1),
|
||||
g.started_time,
|
||||
ms.game_mode,
|
||||
ms.attribs,
|
||||
ms.open_participation,
|
||||
ms.matchmake_system_type,
|
||||
ms.application_buffer,
|
||||
ms.progress_score,
|
||||
ms.session_key,
|
||||
ms.option_zero,
|
||||
ms.matchmake_param,
|
||||
ms.user_password,
|
||||
ms.refer_gid,
|
||||
ms.user_password_enabled,
|
||||
ms.system_password_enabled,
|
||||
ms.codeword,
|
||||
ms.system_password
|
||||
FROM matchmaking.gatherings AS g
|
||||
INNER JOIN matchmaking.matchmake_sessions AS ms ON ms.id = g.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='MatchmakeSession' AND
|
||||
g.id=$1`,
|
||||
gatheringID,
|
||||
).Scan(
|
||||
&resultMatchmakeSession.Gathering.ID,
|
||||
&resultMatchmakeSession.Gathering.OwnerPID,
|
||||
&resultMatchmakeSession.Gathering.HostPID,
|
||||
&resultMatchmakeSession.Gathering.MinimumParticipants,
|
||||
&resultMatchmakeSession.Gathering.MaximumParticipants,
|
||||
&resultMatchmakeSession.Gathering.ParticipationPolicy,
|
||||
&resultMatchmakeSession.Gathering.PolicyArgument,
|
||||
&resultMatchmakeSession.Gathering.Flags,
|
||||
&resultMatchmakeSession.Gathering.State,
|
||||
&resultMatchmakeSession.Gathering.Description,
|
||||
&resultMatchmakeSession.ParticipationCount,
|
||||
&startedTime,
|
||||
&resultMatchmakeSession.GameMode,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultMatchmakeSession.OpenParticipation,
|
||||
&resultMatchmakeSession.MatchmakeSystemType,
|
||||
&resultMatchmakeSession.ApplicationBuffer,
|
||||
&resultMatchmakeSession.ProgressScore,
|
||||
&resultMatchmakeSession.SessionKey,
|
||||
&resultMatchmakeSession.Option,
|
||||
&resultMatchmakeParam,
|
||||
&resultMatchmakeSession.UserPassword,
|
||||
&resultMatchmakeSession.ReferGID,
|
||||
&resultMatchmakeSession.UserPasswordEnabled,
|
||||
&resultMatchmakeSession.SystemPasswordEnabled,
|
||||
&resultMatchmakeSession.CodeWord,
|
||||
&systemPassword,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return match_making_types.NewMatchmakeSession(), "", nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
} else {
|
||||
return match_making_types.NewMatchmakeSession(), "", nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
resultMatchmakeSession.StartedTime = resultMatchmakeSession.StartedTime.FromTimestamp(startedTime)
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultMatchmakeSession.Attributes = attributesSlice
|
||||
|
||||
matchmakeParamBytes := nex.NewByteStreamIn(resultMatchmakeParam, endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
resultMatchmakeSession.MatchmakeParam.ExtractFrom(matchmakeParamBytes)
|
||||
|
||||
return resultMatchmakeSession, systemPassword, nil
|
||||
}
|
60
matchmake-extension/database/get_notification_datas.go
Normal file
60
matchmake-extension/database/get_notification_datas.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetNotificationDatas gets the notification datas that belong to friends of the user and match with any of the given types
|
||||
func GetNotificationDatas(manager *common_globals.MatchmakingManager, sourcePID types.PID, notificationTypes []uint32) ([]notifications_types.NotificationEvent, *nex.Error) {
|
||||
dataList := make([]notifications_types.NotificationEvent, 0)
|
||||
|
||||
var friendList []uint32
|
||||
if manager.GetUserFriendPIDs != nil {
|
||||
friendList = manager.GetUserFriendPIDs(uint32(sourcePID))
|
||||
} else {
|
||||
common_globals.Logger.Warning("GetNotificationDatas missing manager.GetUserFriendPIDs!")
|
||||
}
|
||||
|
||||
// * No friends to check
|
||||
if len(friendList) == 0 {
|
||||
return dataList, nil
|
||||
}
|
||||
|
||||
rows, err := manager.Database.Query(`SELECT
|
||||
source_pid,
|
||||
type,
|
||||
param_1,
|
||||
param_2,
|
||||
param_str
|
||||
FROM matchmaking.notifications WHERE active=true AND source_pid=ANY($1) AND type=ANY($2)
|
||||
`, pqextended.Array(friendList), pqextended.Array(notificationTypes))
|
||||
if err != nil {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
notificationData := notifications_types.NewNotificationEvent()
|
||||
|
||||
err = rows.Scan(
|
||||
¬ificationData.PIDSource,
|
||||
¬ificationData.Type,
|
||||
¬ificationData.Param1,
|
||||
¬ificationData.Param2,
|
||||
¬ificationData.StrParam,
|
||||
)
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
dataList = append(dataList, notificationData)
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
|
||||
return dataList, nil
|
||||
}
|
114
matchmake-extension/database/get_official_communities.go
Normal file
114
matchmake-extension/database/get_official_communities.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetOfficialCommunities returns the official communities based on the given parameters
|
||||
func GetOfficialCommunities(manager *common_globals.MatchmakingManager, sourcePID types.PID, isAvailableOnly bool, resultRange types.ResultRange) ([]match_making_types.PersistentGathering, *nex.Error) {
|
||||
persistentGatherings := make([]match_making_types.PersistentGathering, 0)
|
||||
currentTime := time.Now().UTC()
|
||||
timeNever := types.NewDateTime(0).Standard()
|
||||
rows, err := manager.Database.Query(`SELECT
|
||||
g.id,
|
||||
g.owner_pid,
|
||||
g.host_pid,
|
||||
g.min_participants,
|
||||
g.max_participants,
|
||||
g.participation_policy,
|
||||
g.policy_argument,
|
||||
g.flags,
|
||||
g.state,
|
||||
g.description,
|
||||
pg.community_type,
|
||||
pg.password,
|
||||
pg.attribs,
|
||||
pg.application_buffer,
|
||||
pg.participation_start_date,
|
||||
pg.participation_end_date,
|
||||
(SELECT COUNT(ms.id)
|
||||
FROM matchmaking.matchmake_sessions AS ms
|
||||
INNER JOIN matchmaking.gatherings AS gms ON ms.id = gms.id
|
||||
WHERE gms.registered=true
|
||||
AND ms.matchmake_system_type=5 -- matchmake_system_type=5 is only used in matchmake sessions attached to a persistent gathering
|
||||
AND ms.attribs[1]=g.id) AS matchmake_session_count,
|
||||
COALESCE((SELECT cp.participation_count
|
||||
FROM matchmaking.community_participations AS cp
|
||||
WHERE cp.user_pid=$5
|
||||
AND cp.gathering_id=g.id), 0) AS participation_count
|
||||
FROM matchmaking.gatherings AS g
|
||||
INNER JOIN matchmaking.persistent_gatherings AS pg ON g.id = pg.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='PersistentGathering' AND
|
||||
pg.community_type=2 AND
|
||||
(CASE WHEN $1 THEN
|
||||
(CASE WHEN pg.participation_start_date <> $6 THEN $2 >= pg.participation_start_date ELSE true END)
|
||||
AND
|
||||
(CASE WHEN pg.participation_end_date <> $6 THEN $2 <= pg.participation_end_date ELSE true END)
|
||||
ELSE true END)
|
||||
LIMIT $3 OFFSET $4`,
|
||||
isAvailableOnly,
|
||||
currentTime,
|
||||
uint32(resultRange.Length),
|
||||
uint32(resultRange.Offset),
|
||||
sourcePID,
|
||||
timeNever,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
resultPersistentGathering := match_making_types.NewPersistentGathering()
|
||||
var resultAttribs []uint32
|
||||
var resultParticipationStartDate time.Time
|
||||
var resultParticipationEndDate time.Time
|
||||
|
||||
err = rows.Scan(
|
||||
&resultPersistentGathering.Gathering.ID,
|
||||
&resultPersistentGathering.Gathering.OwnerPID,
|
||||
&resultPersistentGathering.Gathering.HostPID,
|
||||
&resultPersistentGathering.Gathering.MinimumParticipants,
|
||||
&resultPersistentGathering.Gathering.MaximumParticipants,
|
||||
&resultPersistentGathering.Gathering.ParticipationPolicy,
|
||||
&resultPersistentGathering.Gathering.PolicyArgument,
|
||||
&resultPersistentGathering.Gathering.Flags,
|
||||
&resultPersistentGathering.Gathering.State,
|
||||
&resultPersistentGathering.Gathering.Description,
|
||||
&resultPersistentGathering.CommunityType,
|
||||
&resultPersistentGathering.Password,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultPersistentGathering.ApplicationBuffer,
|
||||
&resultParticipationStartDate,
|
||||
&resultParticipationEndDate,
|
||||
&resultPersistentGathering.MatchmakeSessionCount,
|
||||
&resultPersistentGathering.ParticipationCount,
|
||||
)
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultPersistentGathering.Attribs = attributesSlice
|
||||
|
||||
resultPersistentGathering.ParticipationStartDate = resultPersistentGathering.ParticipationStartDate.FromTimestamp(resultParticipationStartDate)
|
||||
resultPersistentGathering.ParticipationEndDate = resultPersistentGathering.ParticipationEndDate.FromTimestamp(resultParticipationEndDate)
|
||||
|
||||
persistentGatherings = append(persistentGatherings, resultPersistentGathering)
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
|
||||
return persistentGatherings, nil
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetPersistentGatheringByGathering gets a persistent gathering with the given gathering data
|
||||
func GetPersistentGatheringByGathering(manager *common_globals.MatchmakingManager, gathering match_making_types.Gathering, sourcePID uint64) (match_making_types.PersistentGathering, *nex.Error) {
|
||||
resultPersistentGathering := match_making_types.NewPersistentGathering()
|
||||
var resultAttribs []uint32
|
||||
var resultParticipationStartDate time.Time
|
||||
var resultParticipationEndDate time.Time
|
||||
|
||||
err := manager.Database.QueryRow(`SELECT
|
||||
community_type,
|
||||
password,
|
||||
attribs,
|
||||
application_buffer,
|
||||
participation_start_date,
|
||||
participation_end_date,
|
||||
(SELECT COUNT(ms.id)
|
||||
FROM matchmaking.matchmake_sessions AS ms
|
||||
INNER JOIN matchmaking.gatherings AS gms ON ms.id = gms.id
|
||||
WHERE gms.registered=true
|
||||
AND ms.matchmake_system_type=5 -- matchmake_system_type=5 is only used in matchmake sessions attached to a persistent gathering
|
||||
AND ms.attribs[1]=g.id) AS matchmake_session_count,
|
||||
COALESCE((SELECT cp.participation_count
|
||||
FROM matchmaking.community_participations AS cp
|
||||
WHERE cp.user_pid=$2
|
||||
AND cp.gathering_id=g.id), 0) AS participation_count
|
||||
FROM matchmaking.persistent_gatherings
|
||||
WHERE id=$1`,
|
||||
gathering.ID,
|
||||
sourcePID,
|
||||
).Scan(
|
||||
&resultPersistentGathering.CommunityType,
|
||||
&resultPersistentGathering.Password,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultPersistentGathering.ApplicationBuffer,
|
||||
&resultParticipationStartDate,
|
||||
&resultParticipationEndDate,
|
||||
&resultPersistentGathering.MatchmakeSessionCount,
|
||||
&resultPersistentGathering.ParticipationCount,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return match_making_types.NewPersistentGathering(), nex.NewError(nex.ResultCodes.RendezVous.InvalidGID, "change_error")
|
||||
} else {
|
||||
return match_making_types.NewPersistentGathering(), nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
resultPersistentGathering.Gathering = gathering
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultPersistentGathering.Attribs = attributesSlice
|
||||
|
||||
resultPersistentGathering.ParticipationStartDate = resultPersistentGathering.ParticipationStartDate.FromTimestamp(resultParticipationStartDate)
|
||||
resultPersistentGathering.ParticipationEndDate = resultPersistentGathering.ParticipationEndDate.FromTimestamp(resultParticipationEndDate)
|
||||
|
||||
return resultPersistentGathering, nil
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetPersistentGatheringByID gets the persistent gatherings from the given gathering IDs
|
||||
func GetPersistentGatheringByID(manager *common_globals.MatchmakingManager, sourcePID types.PID, gatheringID uint32) (match_making_types.PersistentGathering, *nex.Error) {
|
||||
resultPersistentGathering := match_making_types.NewPersistentGathering()
|
||||
var resultAttribs []uint32
|
||||
var resultParticipationStartDate time.Time
|
||||
var resultParticipationEndDate time.Time
|
||||
|
||||
err := manager.Database.QueryRow(`SELECT
|
||||
g.id,
|
||||
g.owner_pid,
|
||||
g.host_pid,
|
||||
g.min_participants,
|
||||
g.max_participants,
|
||||
g.participation_policy,
|
||||
g.policy_argument,
|
||||
g.flags,
|
||||
g.state,
|
||||
g.description,
|
||||
pg.community_type,
|
||||
pg.password,
|
||||
pg.attribs,
|
||||
pg.application_buffer,
|
||||
pg.participation_start_date,
|
||||
pg.participation_end_date,
|
||||
(SELECT COUNT(ms.id)
|
||||
FROM matchmaking.matchmake_sessions AS ms
|
||||
INNER JOIN matchmaking.gatherings AS gms ON ms.id = gms.id
|
||||
WHERE gms.registered=true
|
||||
AND ms.matchmake_system_type=5 -- matchmake_system_type=5 is only used in matchmake sessions attached to a persistent gathering
|
||||
AND ms.attribs[1]=g.id) AS matchmake_session_count,
|
||||
COALESCE((SELECT cp.participation_count
|
||||
FROM matchmaking.community_participations AS cp
|
||||
WHERE cp.user_pid=$2
|
||||
AND cp.gathering_id=g.id), 0) AS participation_count
|
||||
FROM matchmaking.gatherings AS g
|
||||
INNER JOIN matchmaking.persistent_gatherings AS pg ON g.id = pg.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='PersistentGathering' AND
|
||||
g.id=$1`,
|
||||
gatheringID,
|
||||
sourcePID,
|
||||
).Scan(
|
||||
&resultPersistentGathering.Gathering.ID,
|
||||
&resultPersistentGathering.Gathering.OwnerPID,
|
||||
&resultPersistentGathering.Gathering.HostPID,
|
||||
&resultPersistentGathering.Gathering.MinimumParticipants,
|
||||
&resultPersistentGathering.Gathering.MaximumParticipants,
|
||||
&resultPersistentGathering.Gathering.ParticipationPolicy,
|
||||
&resultPersistentGathering.Gathering.PolicyArgument,
|
||||
&resultPersistentGathering.Gathering.Flags,
|
||||
&resultPersistentGathering.Gathering.State,
|
||||
&resultPersistentGathering.Gathering.Description,
|
||||
&resultPersistentGathering.CommunityType,
|
||||
&resultPersistentGathering.Password,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultPersistentGathering.ApplicationBuffer,
|
||||
&resultParticipationStartDate,
|
||||
&resultParticipationEndDate,
|
||||
&resultPersistentGathering.MatchmakeSessionCount,
|
||||
&resultPersistentGathering.ParticipationCount,
|
||||
)
|
||||
if err != nil {
|
||||
return match_making_types.NewPersistentGathering(), nil
|
||||
}
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultPersistentGathering.Attribs = attributesSlice
|
||||
|
||||
resultPersistentGathering.ParticipationStartDate = resultPersistentGathering.ParticipationStartDate.FromTimestamp(resultParticipationStartDate)
|
||||
resultPersistentGathering.ParticipationEndDate = resultPersistentGathering.ParticipationEndDate.FromTimestamp(resultParticipationEndDate)
|
||||
|
||||
return resultPersistentGathering, nil
|
||||
}
|
102
matchmake-extension/database/get_persistent_gatherings_by_id.go
Normal file
102
matchmake-extension/database/get_persistent_gatherings_by_id.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetPersistentGatheringsByID gets the persistent gatherings from the given gathering IDs
|
||||
func GetPersistentGatheringsByID(manager *common_globals.MatchmakingManager, sourcePID types.PID, gatheringIDs []uint32) ([]match_making_types.PersistentGathering, *nex.Error) {
|
||||
persistentGatherings := make([]match_making_types.PersistentGathering, 0)
|
||||
rows, err := manager.Database.Query(`SELECT
|
||||
g.id,
|
||||
g.owner_pid,
|
||||
g.host_pid,
|
||||
g.min_participants,
|
||||
g.max_participants,
|
||||
g.participation_policy,
|
||||
g.policy_argument,
|
||||
g.flags,
|
||||
g.state,
|
||||
g.description,
|
||||
pg.community_type,
|
||||
pg.password,
|
||||
pg.attribs,
|
||||
pg.application_buffer,
|
||||
pg.participation_start_date,
|
||||
pg.participation_end_date,
|
||||
(SELECT COUNT(ms.id)
|
||||
FROM matchmaking.matchmake_sessions AS ms
|
||||
INNER JOIN matchmaking.gatherings AS gms ON ms.id = gms.id
|
||||
WHERE gms.registered=true
|
||||
AND ms.matchmake_system_type=5 -- matchmake_system_type=5 is only used in matchmake sessions attached to a persistent gathering
|
||||
AND ms.attribs[1]=g.id) AS matchmake_session_count,
|
||||
COALESCE((SELECT cp.participation_count
|
||||
FROM matchmaking.community_participations AS cp
|
||||
WHERE cp.user_pid=$2
|
||||
AND cp.gathering_id=g.id), 0) AS participation_count
|
||||
FROM matchmaking.gatherings AS g
|
||||
INNER JOIN matchmaking.persistent_gatherings AS pg ON g.id = pg.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='PersistentGathering' AND
|
||||
g.id=ANY($1)`,
|
||||
pqextended.Array(gatheringIDs),
|
||||
sourcePID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
resultPersistentGathering := match_making_types.NewPersistentGathering()
|
||||
var resultAttribs []uint32
|
||||
var resultParticipationStartDate time.Time
|
||||
var resultParticipationEndDate time.Time
|
||||
|
||||
err = rows.Scan(
|
||||
&resultPersistentGathering.Gathering.ID,
|
||||
&resultPersistentGathering.Gathering.OwnerPID,
|
||||
&resultPersistentGathering.Gathering.HostPID,
|
||||
&resultPersistentGathering.Gathering.MinimumParticipants,
|
||||
&resultPersistentGathering.Gathering.MaximumParticipants,
|
||||
&resultPersistentGathering.Gathering.ParticipationPolicy,
|
||||
&resultPersistentGathering.Gathering.PolicyArgument,
|
||||
&resultPersistentGathering.Gathering.Flags,
|
||||
&resultPersistentGathering.Gathering.State,
|
||||
&resultPersistentGathering.Gathering.Description,
|
||||
&resultPersistentGathering.CommunityType,
|
||||
&resultPersistentGathering.Password,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultPersistentGathering.ApplicationBuffer,
|
||||
&resultParticipationStartDate,
|
||||
&resultParticipationEndDate,
|
||||
&resultPersistentGathering.MatchmakeSessionCount,
|
||||
&resultPersistentGathering.ParticipationCount,
|
||||
)
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultPersistentGathering.Attribs = attributesSlice
|
||||
|
||||
resultPersistentGathering.ParticipationStartDate = resultPersistentGathering.ParticipationStartDate.FromTimestamp(resultParticipationStartDate)
|
||||
resultPersistentGathering.ParticipationEndDate = resultPersistentGathering.ParticipationEndDate.FromTimestamp(resultParticipationEndDate)
|
||||
|
||||
persistentGatherings = append(persistentGatherings, resultPersistentGathering)
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
|
||||
return persistentGatherings, nil
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetPersistentGatheringsByParticipant finds the active persistent gatherings that a user is participating on
|
||||
func GetPersistentGatheringsByParticipant(manager *common_globals.MatchmakingManager, sourcePID types.PID, participant types.PID, resultRange types.ResultRange) ([]match_making_types.PersistentGathering, *nex.Error) {
|
||||
persistentGatherings := make([]match_making_types.PersistentGathering, 0)
|
||||
rows, err := manager.Database.Query(`SELECT
|
||||
g.id,
|
||||
g.owner_pid,
|
||||
g.host_pid,
|
||||
g.min_participants,
|
||||
g.max_participants,
|
||||
g.participation_policy,
|
||||
g.policy_argument,
|
||||
g.flags,
|
||||
g.state,
|
||||
g.description,
|
||||
pg.community_type,
|
||||
pg.password,
|
||||
pg.attribs,
|
||||
pg.application_buffer,
|
||||
pg.participation_start_date,
|
||||
pg.participation_end_date,
|
||||
(SELECT COUNT(ms.id)
|
||||
FROM matchmaking.matchmake_sessions AS ms
|
||||
INNER JOIN matchmaking.gatherings AS gms ON ms.id = gms.id
|
||||
WHERE gms.registered=true
|
||||
AND ms.matchmake_system_type=5 -- matchmake_system_type=5 is only used in matchmake sessions attached to a persistent gathering
|
||||
AND ms.attribs[1]=g.id) AS matchmake_session_count,
|
||||
COALESCE((SELECT cp.participation_count
|
||||
FROM matchmaking.community_participations AS cp
|
||||
WHERE cp.user_pid=$4
|
||||
AND cp.gathering_id=g.id), 0) AS participation_count
|
||||
FROM matchmaking.gatherings AS g
|
||||
INNER JOIN matchmaking.persistent_gatherings AS pg ON g.id = pg.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='PersistentGathering' AND
|
||||
$1=ANY(g.participants)
|
||||
LIMIT $2 OFFSET $3`,
|
||||
participant,
|
||||
resultRange.Length,
|
||||
resultRange.Offset,
|
||||
sourcePID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
resultPersistentGathering := match_making_types.NewPersistentGathering()
|
||||
var resultAttribs []uint32
|
||||
var resultParticipationStartDate time.Time
|
||||
var resultParticipationEndDate time.Time
|
||||
|
||||
err = rows.Scan(
|
||||
&resultPersistentGathering.Gathering.ID,
|
||||
&resultPersistentGathering.Gathering.OwnerPID,
|
||||
&resultPersistentGathering.Gathering.HostPID,
|
||||
&resultPersistentGathering.Gathering.MinimumParticipants,
|
||||
&resultPersistentGathering.Gathering.MaximumParticipants,
|
||||
&resultPersistentGathering.Gathering.ParticipationPolicy,
|
||||
&resultPersistentGathering.Gathering.PolicyArgument,
|
||||
&resultPersistentGathering.Gathering.Flags,
|
||||
&resultPersistentGathering.Gathering.State,
|
||||
&resultPersistentGathering.Gathering.Description,
|
||||
&resultPersistentGathering.CommunityType,
|
||||
&resultPersistentGathering.Password,
|
||||
pqextended.Array(&resultAttribs),
|
||||
&resultPersistentGathering.ApplicationBuffer,
|
||||
&resultParticipationStartDate,
|
||||
&resultParticipationEndDate,
|
||||
&resultPersistentGathering.MatchmakeSessionCount,
|
||||
&resultPersistentGathering.ParticipationCount,
|
||||
)
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
attributesSlice := make([]types.UInt32, len(resultAttribs))
|
||||
for i, value := range resultAttribs {
|
||||
attributesSlice[i] = types.NewUInt32(value)
|
||||
}
|
||||
resultPersistentGathering.Attribs = attributesSlice
|
||||
|
||||
resultPersistentGathering.ParticipationStartDate = resultPersistentGathering.ParticipationStartDate.FromTimestamp(resultParticipationStartDate)
|
||||
resultPersistentGathering.ParticipationEndDate = resultPersistentGathering.ParticipationEndDate.FromTimestamp(resultParticipationEndDate)
|
||||
|
||||
persistentGatherings = append(persistentGatherings, resultPersistentGathering)
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
|
||||
return persistentGatherings, nil
|
||||
}
|
52
matchmake-extension/database/get_simple_communities.go
Normal file
52
matchmake-extension/database/get_simple_communities.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
pqextended "github.com/PretendoNetwork/pq-extended"
|
||||
)
|
||||
|
||||
// GetSimpleCommunities returns a slice of SimpleCommunity using information from the given gathering IDs
|
||||
func GetSimpleCommunities(manager *common_globals.MatchmakingManager, gatheringIDList []uint32) ([]match_making_types.SimpleCommunity, *nex.Error) {
|
||||
simpleCommunities := make([]match_making_types.SimpleCommunity, 0)
|
||||
|
||||
rows, err := manager.Database.Query(`SELECT
|
||||
pg.id,
|
||||
(SELECT COUNT(ms.id)
|
||||
FROM matchmaking.matchmake_sessions AS ms
|
||||
INNER JOIN matchmaking.gatherings AS gms ON ms.id = gms.id
|
||||
WHERE gms.registered=true
|
||||
AND ms.matchmake_system_type=5 -- matchmake_system_type=5 is only used in matchmake sessions attached to a persistent gathering
|
||||
AND ms.attribs[1]=g.id) AS matchmake_session_count
|
||||
FROM matchmaking.persistent_gatherings AS pg
|
||||
INNER JOIN matchmaking.gatherings AS g ON g.id = pg.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='PersistentGathering' AND
|
||||
pg.id=ANY($1)`,
|
||||
pqextended.Array(gatheringIDList),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
resultSimpleCommunity := match_making_types.NewSimpleCommunity()
|
||||
|
||||
err = rows.Scan(
|
||||
&resultSimpleCommunity.GatheringID,
|
||||
&resultSimpleCommunity.MatchmakeSessionCount,
|
||||
)
|
||||
if err != nil {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
simpleCommunities = append(simpleCommunities, resultSimpleCommunity)
|
||||
}
|
||||
|
||||
rows.Close()
|
||||
|
||||
return simpleCommunities, nil
|
||||
}
|
44
matchmake-extension/database/get_simple_playing_session.go
Normal file
44
matchmake-extension/database/get_simple_playing_session.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
)
|
||||
|
||||
// GetSimplePlayingSession returns the simple playing sessions of the given PIDs
|
||||
func GetSimplePlayingSession(manager *common_globals.MatchmakingManager, listPID []types.PID) ([]match_making_types.SimplePlayingSession, *nex.Error) {
|
||||
simplePlayingSessions := make([]match_making_types.SimplePlayingSession, 0)
|
||||
for _, pid := range listPID {
|
||||
simplePlayingSession := match_making_types.NewSimplePlayingSession()
|
||||
|
||||
err := manager.Database.QueryRow(`SELECT
|
||||
g.id,
|
||||
ms.attribs[1],
|
||||
ms.game_mode
|
||||
FROM matchmaking.gatherings AS g
|
||||
INNER JOIN matchmaking.matchmake_sessions AS ms ON ms.id = g.id
|
||||
WHERE
|
||||
g.registered=true AND
|
||||
g.type='MatchmakeSession' AND
|
||||
$1=ANY(g.participants)`, pid).Scan(
|
||||
&simplePlayingSession.GatheringID,
|
||||
&simplePlayingSession.Attribute0,
|
||||
&simplePlayingSession.GameMode)
|
||||
if err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
common_globals.Logger.Critical(err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
simplePlayingSession.PrincipalID = pid
|
||||
|
||||
simplePlayingSessions = append(simplePlayingSessions, simplePlayingSession)
|
||||
}
|
||||
|
||||
return simplePlayingSessions, nil
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// InactivateNotificationDatas marks the notifications of a given user as inactive
|
||||
func InactivateNotificationDatas(manager *common_globals.MatchmakingManager, sourcePID types.PID) *nex.Error {
|
||||
_, err := manager.Database.Exec(`UPDATE matchmaking.notifications SET active=false WHERE source_pid=$1`, sourcePID)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
42
matchmake-extension/database/join_matchmake_session.go
Normal file
42
matchmake-extension/database/join_matchmake_session.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_database "github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/tracking"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/v2/match-making/constants"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
)
|
||||
|
||||
// JoinMatchmakeSession joins participants from the same connection into a MatchmakeSession. Returns the new number of participants
|
||||
func JoinMatchmakeSession(manager *common_globals.MatchmakingManager, matchmakeSession match_making_types.MatchmakeSession, connection *nex.PRUDPConnection, vacantParticipants uint16, joinMessage string) (uint32, *nex.Error) {
|
||||
newParticipants, nexError := match_making_database.JoinGathering(manager, uint32(matchmakeSession.ID), connection, vacantParticipants, joinMessage)
|
||||
if nexError != nil {
|
||||
return 0, nexError
|
||||
}
|
||||
|
||||
// TODO - Should we return the error in these cases?
|
||||
if uint32(matchmakeSession.MatchmakeSystemType) == uint32(constants.MatchmakeSystemTypePersistentGathering) { // * Attached to a persistent gathering
|
||||
persistentGatheringID := uint32(matchmakeSession.Attributes[0])
|
||||
_, nexError = GetPersistentGatheringByID(manager, connection.PID(), persistentGatheringID)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
return newParticipants, nil
|
||||
}
|
||||
|
||||
participationCount, nexError := UpdatePersistentGatheringParticipationCount(manager, connection.PID(), persistentGatheringID)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
return newParticipants, nil
|
||||
}
|
||||
|
||||
nexError = tracking.LogParticipateCommunity(manager.Database, connection.PID(), persistentGatheringID, uint32(matchmakeSession.ID), participationCount)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
return newParticipants, nil
|
||||
}
|
||||
}
|
||||
|
||||
return newParticipants, nil
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
match_making_database "github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/tracking"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/v2/match-making/constants"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
)
|
||||
|
||||
// JoinMatchmakeSessionWithParticipants joins participants into a gathering. Returns the new number of participants
|
||||
func JoinMatchmakeSessionWithParticipants(manager *common_globals.MatchmakingManager, matchmakeSession match_making_types.MatchmakeSession, connection *nex.PRUDPConnection, additionalParticipants []types.PID, joinMessage string, joinMatchmakeSessionBehavior constants.JoinMatchmakeSessionBehavior) (uint32, *nex.Error) {
|
||||
newParticipants, nexError := match_making_database.JoinGatheringWithParticipants(manager, uint32(matchmakeSession.ID), connection, additionalParticipants, joinMessage, joinMatchmakeSessionBehavior)
|
||||
if nexError != nil {
|
||||
return 0, nexError
|
||||
}
|
||||
|
||||
// TODO - Should we return the error in these cases?
|
||||
if uint32(matchmakeSession.MatchmakeSystemType) == uint32(constants.MatchmakeSystemTypePersistentGathering) { // * Attached to a persistent gathering
|
||||
persistentGatheringID := uint32(matchmakeSession.Attributes[0])
|
||||
participantList := append(additionalParticipants, connection.PID())
|
||||
for _, participant := range participantList {
|
||||
_, nexError = GetPersistentGatheringByID(manager, participant, persistentGatheringID)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
participationCount, nexError := UpdatePersistentGatheringParticipationCount(manager, participant, persistentGatheringID)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
nexError = tracking.LogParticipateCommunity(manager.Database, participant, persistentGatheringID, uint32(matchmakeSession.ID), participationCount)
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return newParticipants, nil
|
||||
}
|
26
matchmake-extension/database/update_application_buffer.go
Normal file
26
matchmake-extension/database/update_application_buffer.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// UpdateApplicationBuffer updates the application buffer of a matchmake session
|
||||
func UpdateApplicationBuffer(manager *common_globals.MatchmakingManager, gatheringID uint32, applicationBuffer types.Buffer) *nex.Error {
|
||||
result, err := manager.Database.Exec(`UPDATE matchmaking.matchmake_sessions SET application_buffer=$1 WHERE id=$2`, []byte(applicationBuffer), gatheringID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
25
matchmake-extension/database/update_game_attribute.go
Normal file
25
matchmake-extension/database/update_game_attribute.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// UpdateGameAttribute updates an attribute on a matchmake session
|
||||
func UpdateGameAttribute(manager *common_globals.MatchmakingManager, gatheringID uint32, attributeIndex uint32, newValue uint32) *nex.Error {
|
||||
result, err := manager.Database.Exec(`UPDATE matchmaking.matchmake_sessions SET attribs[$1]=$2 WHERE id=$3`, attributeIndex + 1, newValue, gatheringID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
36
matchmake-extension/database/update_notification_data.go
Normal file
36
matchmake-extension/database/update_notification_data.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// UpdateNotificationData updates the notification data of the specified user and type
|
||||
func UpdateNotificationData(manager *common_globals.MatchmakingManager, notificationData notifications_types.NotificationEvent) *nex.Error {
|
||||
_, err := manager.Database.Exec(`INSERT INTO matchmaking.notifications AS n (
|
||||
source_pid,
|
||||
type,
|
||||
param_1,
|
||||
param_2,
|
||||
param_str
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
) ON CONFLICT (source_pid, type) DO UPDATE SET
|
||||
param_1=$3, param_2=$4, param_str=$5, active=true WHERE n.source_pid=$1 AND n.type=$2`,
|
||||
notificationData.PIDSource,
|
||||
notificationData.Type,
|
||||
notificationData.Param1,
|
||||
notificationData.Param2,
|
||||
notificationData.StrParam,
|
||||
)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
25
matchmake-extension/database/update_participation.go
Normal file
25
matchmake-extension/database/update_participation.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// UpdateParticipation updates the participation of a matchmake session
|
||||
func UpdateParticipation(manager *common_globals.MatchmakingManager, gatheringID uint32, participation bool) *nex.Error {
|
||||
result, err := manager.Database.Exec(`UPDATE matchmaking.matchmake_sessions SET open_participation=$1 WHERE id=$2`, participation, gatheringID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// UpdatePersistentGatheringParticipationCount updates the participation count of a user in a persistent gathering. Returns the participation count
|
||||
func UpdatePersistentGatheringParticipationCount(manager *common_globals.MatchmakingManager, userPID types.PID, gatheringID uint32) (uint32, *nex.Error) {
|
||||
var participationCount uint32
|
||||
err := manager.Database.QueryRow(`INSERT INTO matchmaking.community_participations AS cp (
|
||||
user_pid,
|
||||
gathering_id,
|
||||
participation_count
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
1
|
||||
) ON CONFLICT (user_pid, gathering_id) DO UPDATE SET
|
||||
participation_count=cp.participation_count+1 RETURNING participation_count`,
|
||||
uint64(userPID),
|
||||
gatheringID,
|
||||
).Scan(&participationCount)
|
||||
if err != nil {
|
||||
return 0, nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return participationCount, nil
|
||||
}
|
25
matchmake-extension/database/update_progress_score.go
Normal file
25
matchmake-extension/database/update_progress_score.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
)
|
||||
|
||||
// UpdateProgressScore updates the progress score on a matchmake session
|
||||
func UpdateProgressScore(manager *common_globals.MatchmakingManager, gatheringID uint32, progressScore uint8) *nex.Error {
|
||||
result, err := manager.Database.Exec(`UPDATE matchmaking.matchmake_sessions SET progress_score=$1 WHERE id=$2`, progressScore, gatheringID)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nex.NewError(nex.ResultCodes.RendezVous.SessionVoid, "change_error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
61
matchmake-extension/find_community_by_gathering_id.go
Normal file
61
matchmake-extension/find_community_by_gathering_id.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) findCommunityByGatheringID(err error, packet nex.PacketInterface, callID uint32, lstGID types.List[types.UInt32]) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
var gatheringIDs []uint32
|
||||
for _, gatheringID := range lstGID {
|
||||
gatheringIDs = append(gatheringIDs, uint32(gatheringID))
|
||||
}
|
||||
|
||||
communities, nexError := database.GetPersistentGatheringsByID(commonProtocol.manager, connection.PID(), gatheringIDs)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
lstCommunity := types.NewList[match_making_types.PersistentGathering]()
|
||||
|
||||
for i := range communities {
|
||||
// * Scrap persistent gathering password
|
||||
communities[i].Password = ""
|
||||
}
|
||||
|
||||
lstCommunity = communities
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
lstCommunity.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodFindCommunityByGatheringID
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterFindCommunityByGatheringID != nil {
|
||||
go commonProtocol.OnAfterFindCommunityByGatheringID(packet, lstGID)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
56
matchmake-extension/find_community_by_participant.go
Normal file
56
matchmake-extension/find_community_by_participant.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) findCommunityByParticipant(err error, packet nex.PacketInterface, callID uint32, pid types.PID, resultRange types.ResultRange) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
communities, nexError := database.GetPersistentGatheringsByParticipant(commonProtocol.manager, connection.PID(), pid, resultRange)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
lstCommunity := types.NewList[match_making_types.PersistentGathering]()
|
||||
|
||||
for i := range communities {
|
||||
// * Scrap persistent gathering password
|
||||
communities[i].Password = ""
|
||||
}
|
||||
|
||||
lstCommunity = communities
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
lstCommunity.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodFindCommunityByParticipant
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterFindCommunityByParticipant != nil {
|
||||
go commonProtocol.OnAfterFindCommunityByParticipant(packet, pid, resultRange)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
56
matchmake-extension/find_official_community.go
Normal file
56
matchmake-extension/find_official_community.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) findOfficialCommunity(err error, packet nex.PacketInterface, callID uint32, isAvailableOnly types.Bool, resultRange types.ResultRange) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
communities, nexError := database.GetOfficialCommunities(commonProtocol.manager, connection.PID(), bool(isAvailableOnly), resultRange)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
lstCommunity := types.NewList[match_making_types.PersistentGathering]()
|
||||
|
||||
for i := range communities {
|
||||
// * Scrap persistent gathering password
|
||||
communities[i].Password = ""
|
||||
}
|
||||
|
||||
lstCommunity = communities
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
lstCommunity.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodFindOfficialCommunity
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterFindOfficialCommunity != nil {
|
||||
go commonProtocol.OnAfterFindOfficialCommunity(packet, isAvailableOnly, resultRange)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
56
matchmake-extension/get_friend_notification_data.go
Normal file
56
matchmake-extension/get_friend_notification_data.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) getFriendNotificationData(err error, packet nex.PacketInterface, callID uint32, uiType types.Int32) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
// * This method can only receive notifications within the range 101-108, which are reserved for game-specific notifications
|
||||
if uiType < 101 || uiType > 108 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
notificationDatas, nexError := database.GetNotificationDatas(commonProtocol.manager, connection.PID(), []uint32{notifications.BuildNotificationType(uint32(uiType), 0)})
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
dataList := types.NewList[notifications_types.NotificationEvent]()
|
||||
dataList = notificationDatas
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
dataList.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodGetFriendNotificationData
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterGetFriendNotificationData != nil {
|
||||
go commonProtocol.OnAfterGetFriendNotificationData(packet, uiType)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
61
matchmake-extension/get_lst_friend_notification_data.go
Normal file
61
matchmake-extension/get_lst_friend_notification_data.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) getlstFriendNotificationData(err error, packet nex.PacketInterface, callID uint32, lstTypes types.List[types.UInt32]) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
notificationTypes := make([]uint32, len(lstTypes))
|
||||
for i, notificationType := range lstTypes {
|
||||
// * This method can only receive notifications within the range 101-108, which are reserved for game-specific notifications
|
||||
if notificationType < 101 || notificationType > 108 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
notificationTypes[i] = notifications.BuildNotificationType(uint32(notificationType), 0)
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
notificationDatas, nexError := database.GetNotificationDatas(commonProtocol.manager, connection.PID(), notificationTypes)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
dataList := types.NewList[notifications_types.NotificationEvent]()
|
||||
dataList = notificationDatas
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
dataList.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodGetlstFriendNotificationData
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterGetlstFriendNotificationData != nil {
|
||||
go commonProtocol.OnAfterGetlstFriendNotificationData(packet, lstTypes)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
55
matchmake-extension/get_simple_community.go
Normal file
55
matchmake-extension/get_simple_community.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) getSimpleCommunity(err error, packet nex.PacketInterface, callID uint32, gatheringIDList types.List[types.UInt32]) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
var gatheringIDs []uint32
|
||||
for _, gatheringID := range gatheringIDList {
|
||||
gatheringIDs = append(gatheringIDs, uint32(gatheringID))
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
simpleCommunities, nexError := database.GetSimpleCommunities(commonProtocol.manager, gatheringIDs)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
lstSimpleCommunityList := types.NewList[match_making_types.SimpleCommunity]()
|
||||
lstSimpleCommunityList = simpleCommunities
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
lstSimpleCommunityList.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodGetSimpleCommunity
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterGetSimpleCommunity != nil {
|
||||
go commonProtocol.OnAfterGetSimpleCommunity(packet, gatheringIDList)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
|
@ -1,101 +1,61 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
"golang.org/x/exp/slices"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
// * https://stackoverflow.com/a/70808522
|
||||
func remove[T comparable](l []T, item T) []T {
|
||||
for i, other := range l {
|
||||
if other == item {
|
||||
return append(l[:i], l[i+1:]...)
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func getSimplePlayingSession(err error, client *nex.Client, callID uint32, listPID []uint32, includeLoginUser bool) uint32 {
|
||||
func (commonProtocol *CommonProtocol) getSimplePlayingSession(err error, packet nex.PacketInterface, callID uint32, listPID types.List[types.PID], includeLoginUser types.Bool) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
server := client.Server()
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
if slices.Contains(listPID, client.PID()) {
|
||||
listPID = remove(listPID, client.PID())
|
||||
// * Does nothing if element is not present in the List
|
||||
listPID = slices.DeleteFunc(listPID, func(pid types.PID) bool {
|
||||
return pid == connection.PID()
|
||||
})
|
||||
|
||||
if includeLoginUser {
|
||||
listPID = append(listPID, connection.PID())
|
||||
}
|
||||
|
||||
if includeLoginUser && !slices.Contains(listPID, client.PID()) {
|
||||
listPID = append(listPID, client.PID())
|
||||
commonProtocol.manager.Mutex.RLock()
|
||||
|
||||
simplePlayingSessions, nexError := database.GetSimplePlayingSession(commonProtocol.manager, listPID)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
simplePlayingSessions := make(map[string]*match_making_types.SimplePlayingSession)
|
||||
commonProtocol.manager.Mutex.RUnlock()
|
||||
|
||||
for gatheringID, session := range common_globals.Sessions {
|
||||
for _, pid := range listPID {
|
||||
key := fmt.Sprintf("%d-%d", gatheringID, pid)
|
||||
if simplePlayingSessions[key] == nil {
|
||||
connectedPIDs := make([]uint32, 0)
|
||||
for _, connectionID := range session.ConnectionIDs {
|
||||
player := server.FindClientFromConnectionID(connectionID)
|
||||
lstSimplePlayingSession := types.NewList[match_making_types.SimplePlayingSession]()
|
||||
lstSimplePlayingSession = simplePlayingSessions
|
||||
|
||||
connectedPIDs = append(connectedPIDs, player.PID())
|
||||
}
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
if slices.Contains(connectedPIDs, pid) {
|
||||
simplePlayingSessions[key] = match_making_types.NewSimplePlayingSession()
|
||||
simplePlayingSessions[key].PrincipalID = pid
|
||||
simplePlayingSessions[key].GatheringID = gatheringID
|
||||
simplePlayingSessions[key].GameMode = session.GameMatchmakeSession.GameMode
|
||||
simplePlayingSessions[key].Attribute0 = session.GameMatchmakeSession.Attributes[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lstSimplePlayingSession := make([]*match_making_types.SimplePlayingSession, 0)
|
||||
|
||||
for _, simplePlayingSession := range simplePlayingSessions {
|
||||
lstSimplePlayingSession = append(lstSimplePlayingSession, simplePlayingSession)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(server)
|
||||
|
||||
rmcResponseStream.WriteListStructure(lstSimplePlayingSession)
|
||||
lstSimplePlayingSession.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(matchmake_extension.MethodGetSimplePlayingSession, rmcResponseBody)
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodGetSimplePlayingSession
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
var responsePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
responsePacket, _ = nex.NewPacketV0(client, nil)
|
||||
responsePacket.SetVersion(0)
|
||||
} else {
|
||||
responsePacket, _ = nex.NewPacketV1(client, nil)
|
||||
responsePacket.SetVersion(1)
|
||||
if commonProtocol.OnAfterGetSimplePlayingSession != nil {
|
||||
go commonProtocol.OnAfterGetSimplePlayingSession(packet, listPID, includeLoginUser)
|
||||
}
|
||||
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
return rmcResponse, nil
|
||||
}
|
||||
|
|
78
matchmake-extension/join_matchmake_session.go
Normal file
78
matchmake-extension/join_matchmake_session.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) joinMatchmakeSession(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32, strMessage types.String) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
if len(strMessage) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
server := endpoint.Server
|
||||
|
||||
joinedMatchmakeSession, _, nexError := database.GetMatchmakeSessionByID(commonProtocol.manager, endpoint, uint32(gid))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
// TODO - Is this the correct error code?
|
||||
if joinedMatchmakeSession.UserPasswordEnabled || joinedMatchmakeSession.SystemPasswordEnabled {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
// * Allow game servers to do their own permissions checks
|
||||
if commonProtocol.CanJoinMatchmakeSession != nil {
|
||||
nexError = commonProtocol.CanJoinMatchmakeSession(commonProtocol.manager, connection.PID(), joinedMatchmakeSession)
|
||||
} else {
|
||||
nexError = common_globals.CanJoinMatchmakeSession(commonProtocol.manager, connection.PID(), joinedMatchmakeSession)
|
||||
}
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
_, nexError = database.JoinMatchmakeSession(commonProtocol.manager, joinedMatchmakeSession, connection, 1, string(strMessage))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
if server.LibraryVersions.MatchMaking.GreaterOrEqual("3.0.0") {
|
||||
joinedMatchmakeSession.SessionKey.WriteTo(rmcResponseStream)
|
||||
}
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodJoinMatchmakeSession
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterJoinMatchmakeSession != nil {
|
||||
go commonProtocol.OnAfterJoinMatchmakeSession(packet, gid, strMessage)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
78
matchmake-extension/join_matchmake_session_ex.go
Normal file
78
matchmake-extension/join_matchmake_session_ex.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) joinMatchmakeSessionEx(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32, strMessage types.String, dontCareMyBlockList types.Bool, participationCount types.UInt16) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
if len(strMessage) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
server := endpoint.Server
|
||||
|
||||
joinedMatchmakeSession, _, nexError := database.GetMatchmakeSessionByID(commonProtocol.manager, endpoint, uint32(gid))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
// TODO - Is this the correct error code?
|
||||
if joinedMatchmakeSession.UserPasswordEnabled || joinedMatchmakeSession.SystemPasswordEnabled {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
// * Allow game servers to do their own permissions checks
|
||||
if commonProtocol.CanJoinMatchmakeSession != nil {
|
||||
nexError = commonProtocol.CanJoinMatchmakeSession(commonProtocol.manager, connection.PID(), joinedMatchmakeSession)
|
||||
} else {
|
||||
nexError = common_globals.CanJoinMatchmakeSession(commonProtocol.manager, connection.PID(), joinedMatchmakeSession)
|
||||
}
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
_, nexError = database.JoinMatchmakeSession(commonProtocol.manager, joinedMatchmakeSession, connection, uint16(participationCount), string(strMessage))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
if server.LibraryVersions.MatchMaking.GreaterOrEqual("3.0.0") {
|
||||
joinedMatchmakeSession.SessionKey.WriteTo(rmcResponseStream)
|
||||
}
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodJoinMatchmakeSessionEx
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterJoinMatchmakeSessionEx != nil {
|
||||
go commonProtocol.OnAfterJoinMatchmakeSessionEx(packet, gid, strMessage, dontCareMyBlockList, participationCount)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
|
@ -1,119 +1,90 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/notifications/types"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/v2/match-making/constants"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func joinMatchmakeSessionWithParam(err error, client *nex.Client, callID uint32, joinMatchmakeSessionParam *match_making_types.JoinMatchmakeSessionParam) uint32 {
|
||||
func (commonProtocol *CommonProtocol) joinMatchmakeSessionWithParam(err error, packet nex.PacketInterface, callID uint32, joinMatchmakeSessionParam match_making_types.JoinMatchmakeSessionParam) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
server := client.Server()
|
||||
|
||||
session, ok := common_globals.Sessions[joinMatchmakeSessionParam.GID]
|
||||
if !ok {
|
||||
return nex.Errors.RendezVous.SessionVoid
|
||||
if len(joinMatchmakeSessionParam.JoinMessage) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
// TODO - More checks here
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
err, errCode := common_globals.AddPlayersToSession(session, []uint32{client.ConnectionID()})
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return errCode
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
if joinMatchmakeSessionParam.GIDForParticipationCheck != 0 {
|
||||
// * Check that all new participants are participating in the specified gathering ID
|
||||
nexError := database.CheckGatheringForParticipation(commonProtocol.manager, uint32(joinMatchmakeSessionParam.GIDForParticipationCheck), append(joinMatchmakeSessionParam.AdditionalParticipants, connection.PID()))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
}
|
||||
|
||||
joinedMatchmakeSession := session.GameMatchmakeSession
|
||||
joinedMatchmakeSession, systemPassword, nexError := database.GetMatchmakeSessionByID(commonProtocol.manager, endpoint, uint32(joinMatchmakeSessionParam.GID))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(server)
|
||||
// TODO - Are these the correct error codes?
|
||||
if bool(joinedMatchmakeSession.UserPasswordEnabled) && !joinMatchmakeSessionParam.StrUserPassword.Equals(joinedMatchmakeSession.UserPassword) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.InvalidPassword, "change_error")
|
||||
}
|
||||
|
||||
rmcResponseStream.WriteStructure(joinedMatchmakeSession)
|
||||
if bool(joinedMatchmakeSession.SystemPasswordEnabled) && string(joinMatchmakeSessionParam.StrSystemPassword) != systemPassword {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.InvalidPassword, "change_error")
|
||||
}
|
||||
|
||||
// * Allow game servers to do their own permissions checks
|
||||
if commonProtocol.CanJoinMatchmakeSession != nil {
|
||||
nexError = commonProtocol.CanJoinMatchmakeSession(commonProtocol.manager, connection.PID(), joinedMatchmakeSession)
|
||||
} else {
|
||||
nexError = common_globals.CanJoinMatchmakeSession(commonProtocol.manager, connection.PID(), joinedMatchmakeSession)
|
||||
}
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
_, nexError = database.JoinMatchmakeSessionWithParticipants(commonProtocol.manager, joinedMatchmakeSession, connection, joinMatchmakeSessionParam.AdditionalParticipants, string(joinMatchmakeSessionParam.JoinMessage), constants.JoinMatchmakeSessionBehavior(joinMatchmakeSessionParam.JoinMatchmakeSessionBehavior))
|
||||
if nexError != nil {
|
||||
common_globals.Logger.Error(nexError.Error())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponseStream := nex.NewByteStreamOut(endpoint.LibraryVersions(), endpoint.ByteStreamSettings())
|
||||
|
||||
joinedMatchmakeSession.WriteTo(rmcResponseStream)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(matchmake_extension.MethodJoinMatchmakeSessionWithParam, rmcResponseBody)
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, rmcResponseBody)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodJoinMatchmakeSessionWithParam
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
var responsePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
responsePacket, _ = nex.NewPacketV0(client, nil)
|
||||
responsePacket.SetVersion(0)
|
||||
} else {
|
||||
responsePacket, _ = nex.NewPacketV1(client, nil)
|
||||
responsePacket.SetVersion(1)
|
||||
if commonProtocol.OnAfterJoinMatchmakeSessionWithParam != nil {
|
||||
go commonProtocol.OnAfterJoinMatchmakeSessionWithParam(packet, joinMatchmakeSessionParam)
|
||||
}
|
||||
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(responsePacket)
|
||||
|
||||
for i := 0; i < len(session.ConnectionIDs); i++ {
|
||||
target := server.FindClientFromConnectionID(session.ConnectionIDs[i])
|
||||
if target == nil {
|
||||
// TODO - Error here?
|
||||
continue
|
||||
}
|
||||
|
||||
// * Works for Minecraft, not tried on anything else
|
||||
notificationRequestMessage := nex.NewRMCRequest()
|
||||
notificationRequestMessage.SetProtocolID(notifications.ProtocolID)
|
||||
notificationRequestMessage.SetCallID(0xffff0000 + callID + uint32(i))
|
||||
notificationRequestMessage.SetMethodID(notifications.MethodProcessNotificationEvent)
|
||||
|
||||
notificationCategory := notifications.NotificationCategories.Participation
|
||||
notificationSubtype := notifications.NotificationSubTypes.Participation.NewParticipant
|
||||
|
||||
oEvent := notifications_types.NewNotificationEvent()
|
||||
oEvent.PIDSource = client.PID()
|
||||
oEvent.Type = notifications.BuildNotificationType(notificationCategory, notificationSubtype)
|
||||
oEvent.Param1 = joinedMatchmakeSession.ID
|
||||
oEvent.Param2 = target.PID()
|
||||
oEvent.StrParam = ""
|
||||
oEvent.Param3 = 1
|
||||
|
||||
notificationStream := nex.NewStreamOut(server)
|
||||
|
||||
notificationStream.WriteStructure(oEvent)
|
||||
|
||||
notificationRequestMessage.SetParameters(notificationStream.Bytes())
|
||||
notificationRequestBytes := notificationRequestMessage.Bytes()
|
||||
|
||||
var messagePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
messagePacket, _ = nex.NewPacketV0(client, nil)
|
||||
messagePacket.SetVersion(0)
|
||||
} else {
|
||||
messagePacket, _ = nex.NewPacketV1(client, nil)
|
||||
messagePacket.SetVersion(1)
|
||||
}
|
||||
|
||||
messagePacket.SetSource(0xA1)
|
||||
messagePacket.SetDestination(0xAF)
|
||||
messagePacket.SetType(nex.DataPacket)
|
||||
messagePacket.SetPayload(notificationRequestBytes)
|
||||
|
||||
messagePacket.AddFlag(nex.FlagNeedsAck)
|
||||
messagePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
server.Send(messagePacket)
|
||||
}
|
||||
|
||||
return 0
|
||||
return rmcResponse, nil
|
||||
}
|
||||
|
|
58
matchmake-extension/modify_current_game_attribute.go
Normal file
58
matchmake-extension/modify_current_game_attribute.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) modifyCurrentGameAttribute(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32, attribIndex types.UInt32, newValue types.UInt32) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
session, _, nexError := database.GetMatchmakeSessionByID(commonProtocol.manager, endpoint, uint32(gid))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
if !session.Gathering.OwnerPID.Equals(connection.PID()) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
index := int(attribIndex)
|
||||
|
||||
if index >= len(session.Attributes) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidIndex, "change_error")
|
||||
}
|
||||
|
||||
nexError = database.UpdateGameAttribute(commonProtocol.manager, uint32(gid), uint32(attribIndex), uint32(newValue))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodModifyCurrentGameAttribute
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterModifyCurrentGameAttribute != nil {
|
||||
go commonProtocol.OnAfterModifyCurrentGameAttribute(packet, gid, attribIndex, newValue)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
|
@ -1,55 +1,52 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func openParticipation(err error, client *nex.Client, callID uint32, gid uint32) uint32 {
|
||||
func (commonProtocol *CommonProtocol) openParticipation(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
var session *common_globals.CommonMatchmakeSession
|
||||
var ok bool
|
||||
if session, ok = common_globals.Sessions[gid]; !ok {
|
||||
return nex.Errors.RendezVous.SessionVoid
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
session, _, nexError := database.GetMatchmakeSessionByID(commonProtocol.manager, endpoint, uint32(gid))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
if session.GameMatchmakeSession.Gathering.OwnerPID != client.PID() {
|
||||
return nex.Errors.RendezVous.PermissionDenied
|
||||
if !session.Gathering.OwnerPID.Equals(connection.PID()) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
session.GameMatchmakeSession.OpenParticipation = true
|
||||
|
||||
server := commonMatchmakeExtensionProtocol.server
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(matchmake_extension.MethodOpenParticipation, nil)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
var responsePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
responsePacket, _ = nex.NewPacketV0(client, nil)
|
||||
responsePacket.SetVersion(0)
|
||||
} else {
|
||||
responsePacket, _ = nex.NewPacketV1(client, nil)
|
||||
responsePacket.SetVersion(1)
|
||||
nexError = database.UpdateParticipation(commonProtocol.manager, uint32(gid), true)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodOpenParticipation
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
server.Send(responsePacket)
|
||||
if commonProtocol.OnAfterOpenParticipation != nil {
|
||||
go commonProtocol.OnAfterOpenParticipation(packet, gid)
|
||||
}
|
||||
|
||||
return 0
|
||||
return rmcResponse, nil
|
||||
}
|
||||
|
|
|
@ -1,77 +1,203 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
matchmake_extension_mario_kart_8 "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension/mario-kart-8"
|
||||
"github.com/PretendoNetwork/plogger-go"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
match_making_types "github.com/PretendoNetwork/nex-protocols-go/v2/match-making/types"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
)
|
||||
|
||||
var commonMatchmakeExtensionProtocol *CommonMatchmakeExtensionProtocol
|
||||
var logger = plogger.NewLogger()
|
||||
|
||||
type CommonMatchmakeExtensionProtocol struct {
|
||||
server *nex.Server
|
||||
DefaultProtocol *matchmake_extension.Protocol
|
||||
MarioKart8Protocol *matchmake_extension_mario_kart_8.Protocol
|
||||
|
||||
cleanupSearchMatchmakeSessionHandler func(matchmakeSession *match_making_types.MatchmakeSession)
|
||||
cleanupMatchmakeSessionSearchCriteriaHandler func(lstSearchCriteria []*match_making_types.MatchmakeSessionSearchCriteria)
|
||||
gameSpecificMatchmakeSessionSearcgCriteriaChecksHandler func(requestSearchCriteria, sessionSearchCriteria *match_making_types.MatchmakeSessionSearchCriteria) bool
|
||||
type CommonProtocol struct {
|
||||
endpoint nex.EndpointInterface
|
||||
protocol matchmake_extension.Interface
|
||||
manager *common_globals.MatchmakingManager
|
||||
PersistentGatheringCreationMax int
|
||||
CanJoinMatchmakeSession func(manager *common_globals.MatchmakingManager, pid types.PID, matchmakeSession match_making_types.MatchmakeSession) *nex.Error
|
||||
CleanupSearchMatchmakeSession func(matchmakeSession *match_making_types.MatchmakeSession)
|
||||
CleanupMatchmakeSessionSearchCriterias func(searchCriterias types.List[match_making_types.MatchmakeSessionSearchCriteria])
|
||||
OnAfterOpenParticipation func(packet nex.PacketInterface, gid types.UInt32)
|
||||
OnAfterCloseParticipation func(packet nex.PacketInterface, gid types.UInt32)
|
||||
OnAfterCreateMatchmakeSession func(packet nex.PacketInterface, anyGathering match_making_types.GatheringHolder, message types.String, participationCount types.UInt16)
|
||||
OnAfterGetSimplePlayingSession func(packet nex.PacketInterface, listPID types.List[types.PID], includeLoginUser types.Bool)
|
||||
OnAfterAutoMatchmakePostpone func(packet nex.PacketInterface, anyGathering match_making_types.GatheringHolder, message types.String)
|
||||
OnAfterAutoMatchmakeWithParamPostpone func(packet nex.PacketInterface, autoMatchmakeParam match_making_types.AutoMatchmakeParam)
|
||||
OnAfterAutoMatchmakeWithSearchCriteriaPostpone func(packet nex.PacketInterface, lstSearchCriteria types.List[match_making_types.MatchmakeSessionSearchCriteria], anyGathering match_making_types.GatheringHolder, strMessage types.String)
|
||||
OnAfterCreateCommunity func(packet nex.PacketInterface, community match_making_types.PersistentGathering, strMessage types.String)
|
||||
OnAfterFindCommunityByGatheringID func(packet nex.PacketInterface, lstGID types.List[types.UInt32])
|
||||
OnAfterFindOfficialCommunity func(packet nex.PacketInterface, isAvailableOnly types.Bool, resultRange types.ResultRange)
|
||||
OnAfterFindCommunityByParticipant func(packet nex.PacketInterface, pid types.PID, resultRange types.ResultRange)
|
||||
OnAfterUpdateProgressScore func(packet nex.PacketInterface, gid types.UInt32, progressScore types.UInt8)
|
||||
OnAfterCreateMatchmakeSessionWithParam func(packet nex.PacketInterface, createMatchmakeSessionParam match_making_types.CreateMatchmakeSessionParam)
|
||||
OnAfterUpdateApplicationBuffer func(packet nex.PacketInterface, gid types.UInt32, applicationBuffer types.Buffer)
|
||||
OnAfterJoinMatchmakeSession func(packet nex.PacketInterface, gid types.UInt32, strMessage types.String)
|
||||
OnAfterJoinMatchmakeSessionWithParam func(packet nex.PacketInterface, joinMatchmakeSessionParam match_making_types.JoinMatchmakeSessionParam)
|
||||
OnAfterModifyCurrentGameAttribute func(packet nex.PacketInterface, gid types.UInt32, attribIndex types.UInt32, newValue types.UInt32)
|
||||
OnAfterBrowseMatchmakeSession func(packet nex.PacketInterface, searchCriteria match_making_types.MatchmakeSessionSearchCriteria, resultRange types.ResultRange)
|
||||
OnAfterJoinMatchmakeSessionEx func(packet nex.PacketInterface, gid types.UInt32, strMessage types.String, dontCareMyBlockList types.Bool, participationCount types.UInt16)
|
||||
OnAfterGetSimpleCommunity func(packet nex.PacketInterface, gatheringIDList types.List[types.UInt32])
|
||||
OnAfterUpdateNotificationData func(packet nex.PacketInterface, uiType types.UInt32, uiParam1 types.UInt32, uiParam2 types.UInt32, strParam types.String)
|
||||
OnAfterGetFriendNotificationData func(packet nex.PacketInterface, uiType types.Int32)
|
||||
OnAfterGetlstFriendNotificationData func(packet nex.PacketInterface, lstTypes types.List[types.UInt32])
|
||||
}
|
||||
|
||||
// CleanupSearchMatchmakeSession sets the CleanupSearchMatchmakeSession handler function
|
||||
func (commonMatchmakeExtensionProtocol *CommonMatchmakeExtensionProtocol) CleanupSearchMatchmakeSession(handler func(matchmakeSession *match_making_types.MatchmakeSession)) {
|
||||
commonMatchmakeExtensionProtocol.cleanupSearchMatchmakeSessionHandler = handler
|
||||
}
|
||||
// SetDatabase defines the matchmaking manager to be used by the common protocol
|
||||
func (commonProtocol *CommonProtocol) SetManager(manager *common_globals.MatchmakingManager) {
|
||||
var err error
|
||||
|
||||
// CleanupMatchmakeSessionSearchCriteria sets the CleanupMatchmakeSessionSearchCriteria handler function
|
||||
func (commonMatchmakeExtensionProtocol *CommonMatchmakeExtensionProtocol) CleanupMatchmakeSessionSearchCriteria(handler func(lstSearchCriteria []*match_making_types.MatchmakeSessionSearchCriteria)) {
|
||||
commonMatchmakeExtensionProtocol.cleanupMatchmakeSessionSearchCriteriaHandler = handler
|
||||
}
|
||||
commonProtocol.manager = manager
|
||||
|
||||
// GameSpecificMatchmakeSessionSearcgCriteriaChecks sets the GameSpecificMatchmakeSessionSearcgCriteriaChecks handler function
|
||||
func (commonMatchmakeExtensionProtocol *CommonMatchmakeExtensionProtocol) GameSpecificMatchmakeSessionSearcgCriteriaChecks(handler func(requestSearchCriteria, sessionSearchCriteria *match_making_types.MatchmakeSessionSearchCriteria) bool) {
|
||||
commonMatchmakeExtensionProtocol.gameSpecificMatchmakeSessionSearcgCriteriaChecksHandler = handler
|
||||
}
|
||||
manager.GetDetailedGatheringByID = database.GetDetailedGatheringByID
|
||||
|
||||
func initDefault(c *CommonMatchmakeExtensionProtocol) {
|
||||
// TODO - Organize by method ID
|
||||
c.DefaultProtocol = matchmake_extension.NewProtocol(c.server)
|
||||
c.DefaultProtocol.OpenParticipation(openParticipation)
|
||||
c.DefaultProtocol.CreateMatchmakeSession(createMatchmakeSession)
|
||||
c.DefaultProtocol.GetSimplePlayingSession(getSimplePlayingSession)
|
||||
c.DefaultProtocol.AutoMatchmakePostpone(autoMatchmake_Postpone)
|
||||
c.DefaultProtocol.AutoMatchmakeWithSearchCriteriaPostpone(autoMatchmakeWithSearchCriteria_Postpone)
|
||||
c.DefaultProtocol.UpdateProgressScore(updateProgressScore)
|
||||
c.DefaultProtocol.CreateMatchmakeSessionWithParam(createMatchmakeSessionWithParam)
|
||||
c.DefaultProtocol.UpdateApplicationBuffer(updateApplicationBuffer)
|
||||
c.DefaultProtocol.JoinMatchmakeSessionWithParam(joinMatchmakeSessionWithParam)
|
||||
}
|
||||
|
||||
func initMarioKart8(c *CommonMatchmakeExtensionProtocol) {
|
||||
// TODO - Organize by method ID
|
||||
c.MarioKart8Protocol = matchmake_extension_mario_kart_8.NewProtocol(c.server)
|
||||
c.MarioKart8Protocol.OpenParticipation(openParticipation)
|
||||
c.MarioKart8Protocol.CreateMatchmakeSession(createMatchmakeSession)
|
||||
c.MarioKart8Protocol.GetSimplePlayingSession(getSimplePlayingSession)
|
||||
c.MarioKart8Protocol.AutoMatchmakePostpone(autoMatchmake_Postpone)
|
||||
c.MarioKart8Protocol.AutoMatchmakeWithSearchCriteriaPostpone(autoMatchmakeWithSearchCriteria_Postpone)
|
||||
c.MarioKart8Protocol.UpdateProgressScore(updateProgressScore)
|
||||
}
|
||||
|
||||
// NewCommonMatchmakeExtensionProtocol returns a new CommonMatchmakeExtensionProtocol
|
||||
func NewCommonMatchmakeExtensionProtocol(server *nex.Server, patch string) *CommonMatchmakeExtensionProtocol {
|
||||
commonMatchmakeExtensionProtocol = &CommonMatchmakeExtensionProtocol{server: server}
|
||||
|
||||
switch patch {
|
||||
case "mario-kart-8":
|
||||
initMarioKart8(commonMatchmakeExtensionProtocol)
|
||||
default:
|
||||
logger.Infof("Patch %q not recognized. Using default protocol", patch)
|
||||
initDefault(commonMatchmakeExtensionProtocol)
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS matchmaking.matchmake_sessions (
|
||||
id bigserial PRIMARY KEY,
|
||||
game_mode bigint,
|
||||
attribs bigint[],
|
||||
open_participation boolean,
|
||||
matchmake_system_type bigint,
|
||||
application_buffer bytea,
|
||||
flags bigint,
|
||||
state bigint,
|
||||
progress_score smallint,
|
||||
session_key bytea,
|
||||
option_zero bigint,
|
||||
matchmake_param bytea,
|
||||
user_password text,
|
||||
refer_gid bigint,
|
||||
user_password_enabled boolean,
|
||||
system_password_enabled boolean,
|
||||
codeword text,
|
||||
system_password text NOT NULL DEFAULT ''
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
return commonMatchmakeExtensionProtocol
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS matchmaking.persistent_gatherings (
|
||||
id bigserial PRIMARY KEY,
|
||||
community_type bigint,
|
||||
password text,
|
||||
attribs bigint[],
|
||||
application_buffer bytea,
|
||||
participation_start_date timestamp,
|
||||
participation_end_date timestamp
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS matchmaking.community_participations (
|
||||
id bigserial PRIMARY KEY,
|
||||
user_pid numeric(10),
|
||||
gathering_id bigint,
|
||||
participation_count bigint,
|
||||
UNIQUE (user_pid, gathering_id)
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS matchmaking.notifications (
|
||||
id bigserial PRIMARY KEY,
|
||||
source_pid numeric(10),
|
||||
type bigint,
|
||||
param_1 bigint,
|
||||
param_2 bigint,
|
||||
param_str text,
|
||||
active boolean NOT NULL DEFAULT true,
|
||||
UNIQUE (source_pid, type)
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.participate_community (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
community_gid bigint,
|
||||
gathering_id bigint,
|
||||
participation_count bigint
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = manager.Database.Exec(`CREATE TABLE IF NOT EXISTS tracking.notification_data (
|
||||
id bigserial PRIMARY KEY,
|
||||
date timestamp,
|
||||
source_pid numeric(10),
|
||||
type bigint,
|
||||
param_1 bigint,
|
||||
param_2 bigint,
|
||||
param_str text
|
||||
)`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// * In case the server is restarted, unregister any previous matchmake sessions
|
||||
_, err = manager.Database.Exec(`UPDATE matchmaking.gatherings SET registered=false WHERE type='MatchmakeSession'`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// * Mark all notifications as inactive
|
||||
_, err = manager.Database.Exec(`UPDATE matchmaking.notifications SET active=false`)
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// NewCommonProtocol returns a new CommonProtocol
|
||||
func NewCommonProtocol(protocol matchmake_extension.Interface) *CommonProtocol {
|
||||
endpoint := protocol.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
commonProtocol := &CommonProtocol{
|
||||
endpoint: endpoint,
|
||||
protocol: protocol,
|
||||
PersistentGatheringCreationMax: 4, // * Default of 4 active persistent gatherings per user
|
||||
}
|
||||
|
||||
protocol.SetHandlerOpenParticipation(commonProtocol.openParticipation)
|
||||
protocol.SetHandlerCloseParticipation(commonProtocol.closeParticipation)
|
||||
protocol.SetHandlerCreateMatchmakeSession(commonProtocol.createMatchmakeSession)
|
||||
protocol.SetHandlerGetSimplePlayingSession(commonProtocol.getSimplePlayingSession)
|
||||
protocol.SetHandlerAutoMatchmakePostpone(commonProtocol.autoMatchmakePostpone)
|
||||
protocol.SetHandlerAutoMatchmakeWithParamPostpone(commonProtocol.autoMatchmakeWithParamPostpone)
|
||||
protocol.SetHandlerAutoMatchmakeWithSearchCriteriaPostpone(commonProtocol.autoMatchmakeWithSearchCriteriaPostpone)
|
||||
protocol.SetHandlerCreateCommunity(commonProtocol.createCommunity)
|
||||
protocol.SetHandlerFindCommunityByGatheringID(commonProtocol.findCommunityByGatheringID)
|
||||
protocol.SetHandlerFindOfficialCommunity(commonProtocol.findOfficialCommunity)
|
||||
protocol.SetHandlerFindCommunityByParticipant(commonProtocol.findCommunityByParticipant)
|
||||
protocol.SetHandlerUpdateProgressScore(commonProtocol.updateProgressScore)
|
||||
protocol.SetHandlerCreateMatchmakeSessionWithParam(commonProtocol.createMatchmakeSessionWithParam)
|
||||
protocol.SetHandlerUpdateApplicationBuffer(commonProtocol.updateApplicationBuffer)
|
||||
protocol.SetHandlerJoinMatchmakeSession(commonProtocol.joinMatchmakeSession)
|
||||
protocol.SetHandlerJoinMatchmakeSessionWithParam(commonProtocol.joinMatchmakeSessionWithParam)
|
||||
protocol.SetHandlerModifyCurrentGameAttribute(commonProtocol.modifyCurrentGameAttribute)
|
||||
protocol.SetHandlerBrowseMatchmakeSession(commonProtocol.browseMatchmakeSession)
|
||||
protocol.SetHandlerJoinMatchmakeSessionEx(commonProtocol.joinMatchmakeSessionEx)
|
||||
protocol.SetHandlerGetSimpleCommunity(commonProtocol.getSimpleCommunity)
|
||||
protocol.SetHandlerUpdateNotificationData(commonProtocol.updateNotificationData)
|
||||
protocol.SetHandlerGetFriendNotificationData(commonProtocol.getFriendNotificationData)
|
||||
protocol.SetHandlerGetlstFriendNotificationData(commonProtocol.getlstFriendNotificationData)
|
||||
|
||||
endpoint.OnConnectionEnded(func(connection *nex.PRUDPConnection) {
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
database.InactivateNotificationDatas(commonProtocol.manager, connection.PID())
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
})
|
||||
|
||||
return commonProtocol
|
||||
}
|
||||
|
|
42
matchmake-extension/tracking/log_notification_data.go
Normal file
42
matchmake-extension/tracking/log_notification_data.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
// LogNotificationData logs the update of the notification data of a user with UpdateNotificationData
|
||||
func LogNotificationData(db *sql.DB, notificationData notifications_types.NotificationEvent) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.notification_data (
|
||||
date,
|
||||
source_pid,
|
||||
type,
|
||||
param_1,
|
||||
param_2,
|
||||
param_str
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6
|
||||
)`,
|
||||
eventTime,
|
||||
notificationData.PIDSource,
|
||||
notificationData.Type,
|
||||
notificationData.Param1,
|
||||
notificationData.Param2,
|
||||
notificationData.StrParam,
|
||||
)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
33
matchmake-extension/tracking/log_participate_community.go
Normal file
33
matchmake-extension/tracking/log_participate_community.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package tracking
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
)
|
||||
|
||||
// LogParticipateCommunity logs a persistent gathering participation event on the given database
|
||||
func LogParticipateCommunity(db *sql.DB, sourcePID types.PID, communityGID uint32, gatheringID uint32, participationCount uint32) *nex.Error {
|
||||
eventTime := time.Now().UTC()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO tracking.participate_community (
|
||||
date,
|
||||
source_pid,
|
||||
community_gid,
|
||||
gathering_id,
|
||||
participation_count
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
)`, eventTime, uint64(sourcePID), communityGID, gatheringID, participationCount)
|
||||
if err != nil {
|
||||
return nex.NewError(nex.ResultCodes.Core.Unknown, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,50 +1,51 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/globals"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/matchmake-extension"
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
)
|
||||
|
||||
func updateApplicationBuffer(err error, client *nex.Client, callID uint32, gid uint32, applicationBuffer []byte) uint32 {
|
||||
func (commonProtocol *CommonProtocol) updateApplicationBuffer(err error, packet nex.PacketInterface, callID uint32, gid types.UInt32, applicationBuffer types.Buffer) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nex.Errors.Core.InvalidArgument
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
server := client.Server()
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
session, ok := common_globals.Sessions[gid]
|
||||
if !ok {
|
||||
return nex.Errors.RendezVous.SessionVoid
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
session, _, nexError := database.GetMatchmakeSessionByID(commonProtocol.manager, endpoint, uint32(gid))
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
session.GameMatchmakeSession.ApplicationData = applicationBuffer
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(matchmake_extension.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(matchmake_extension.MethodOpenParticipation, nil)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
var responsePacket nex.PacketInterface
|
||||
|
||||
if server.PRUDPVersion() == 0 {
|
||||
responsePacket, _ = nex.NewPacketV0(client, nil)
|
||||
responsePacket.SetVersion(0)
|
||||
} else {
|
||||
responsePacket, _ = nex.NewPacketV1(client, nil)
|
||||
responsePacket.SetVersion(1)
|
||||
if !session.Gathering.OwnerPID.Equals(connection.PID()) {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "change_error")
|
||||
}
|
||||
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
nexError = database.UpdateApplicationBuffer(commonProtocol.manager, uint32(gid), applicationBuffer)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
server.Send(responsePacket)
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodUpdateApplicationBuffer
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
return 0
|
||||
if commonProtocol.OnAfterUpdateApplicationBuffer != nil {
|
||||
go commonProtocol.OnAfterUpdateApplicationBuffer(packet, gid, applicationBuffer)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
||||
|
|
87
matchmake-extension/update_notification_data.go
Normal file
87
matchmake-extension/update_notification_data.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
package matchmake_extension
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
common_globals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/database"
|
||||
"github.com/PretendoNetwork/nex-protocols-common-go/v2/matchmake-extension/tracking"
|
||||
matchmake_extension "github.com/PretendoNetwork/nex-protocols-go/v2/matchmake-extension"
|
||||
notifications "github.com/PretendoNetwork/nex-protocols-go/v2/notifications"
|
||||
notifications_types "github.com/PretendoNetwork/nex-protocols-go/v2/notifications/types"
|
||||
)
|
||||
|
||||
func (commonProtocol *CommonProtocol) updateNotificationData(err error, packet nex.PacketInterface, callID uint32, uiType types.UInt32, uiParam1 types.UInt32, uiParam2 types.UInt32, strParam types.String) (*nex.RMCMessage, *nex.Error) {
|
||||
if err != nil {
|
||||
common_globals.Logger.Error(err.Error())
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
// * This method can only send notifications within the range 101-108, which are reserved for game-specific notifications
|
||||
if uiType < 101 || uiType > 108 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
// * All strings must have a length lower than 256.
|
||||
// * Kid Icarus: Uprising sends strings with UTF-8 bytes longer than 256, so I assume this should count the runes instead
|
||||
if utf8.RuneCountInString(string(strParam)) > 256 {
|
||||
return nil, nex.NewError(nex.ResultCodes.Core.InvalidArgument, "change_error")
|
||||
}
|
||||
|
||||
connection := packet.Sender().(*nex.PRUDPConnection)
|
||||
endpoint := connection.Endpoint().(*nex.PRUDPEndPoint)
|
||||
|
||||
notificationData := notifications_types.NewNotificationEvent()
|
||||
notificationData.PIDSource = connection.PID()
|
||||
notificationData.Type = types.NewUInt32(notifications.BuildNotificationType(uint32(uiType), 0))
|
||||
notificationData.Param1 = uiParam1
|
||||
notificationData.Param2 = uiParam2
|
||||
notificationData.StrParam = strParam
|
||||
|
||||
commonProtocol.manager.Mutex.Lock()
|
||||
|
||||
nexError := database.UpdateNotificationData(commonProtocol.manager, notificationData)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
nexError = tracking.LogNotificationData(commonProtocol.manager.Database, notificationData)
|
||||
if nexError != nil {
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
return nil, nexError
|
||||
}
|
||||
|
||||
commonProtocol.manager.Mutex.Unlock()
|
||||
|
||||
// * If the friends are connected, try to send the notifications directly aswell. This is observed on Mario Tennis Open
|
||||
var friendList []uint32
|
||||
if commonProtocol.manager.GetUserFriendPIDs != nil {
|
||||
friendList = commonProtocol.manager.GetUserFriendPIDs(uint32(connection.PID()))
|
||||
}
|
||||
|
||||
if len(friendList) != 0 {
|
||||
var targets []uint64
|
||||
for _, pid := range friendList {
|
||||
// * Only send the notification to friends who are connected
|
||||
if endpoint.FindConnectionByPID(uint64(pid)) != nil {
|
||||
targets = append(targets, uint64(pid))
|
||||
}
|
||||
}
|
||||
|
||||
common_globals.SendNotificationEvent(endpoint, notificationData, targets)
|
||||
}
|
||||
|
||||
rmcResponse := nex.NewRMCSuccess(endpoint, nil)
|
||||
rmcResponse.ProtocolID = matchmake_extension.ProtocolID
|
||||
rmcResponse.MethodID = matchmake_extension.MethodUpdateNotificationData
|
||||
rmcResponse.CallID = callID
|
||||
|
||||
if commonProtocol.OnAfterUpdateNotificationData != nil {
|
||||
go commonProtocol.OnAfterUpdateNotificationData(packet, uiType, uiParam1, uiParam2, strParam)
|
||||
}
|
||||
|
||||
return rmcResponse, nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue