mirror of
https://github.com/PretendoNetwork/super-mario-maker.git
synced 2026-09-30 03:46:37 -05:00
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
@@ -5,14 +8,21 @@
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# custom
|
||||
build
|
||||
.vscode
|
||||
.env
|
||||
*.key
|
||||
build
|
||||
log
|
||||
60
Makefile
Normal file
60
Makefile
Normal file
@@ -0,0 +1,60 @@
|
||||
# TODO - Assumes a UNIX-like OS
|
||||
|
||||
RED := $(shell tput setaf 1)
|
||||
BLUE := $(shell tput setaf 4)
|
||||
CYAN := $(shell tput setaf 14)
|
||||
ORANGE := $(shell tput setaf 202)
|
||||
YELLOW := $(shell tput setaf 214)
|
||||
RESET := $(shell tput sgr0)
|
||||
|
||||
ifeq ($(shell which go),)
|
||||
# TODO - Read contents from .git folder instead?
|
||||
$(error "$(RED)go command not found. Install go to continue $(BLUE)https://go.dev/doc/install$(RESET)")
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard .git),)
|
||||
# * .git folder exists, build server build string from repo info
|
||||
ifeq ($(shell which git),)
|
||||
# TODO - Read contents from .git folder instead?
|
||||
$(error "$(RED)git command not found. Install git to continue $(ORANGE)https://git-scm.com/downloads$(RESET)")
|
||||
endif
|
||||
$(info "$(CYAN)Building server build string from repository info$(RESET)")
|
||||
# * Build server build string from repo info
|
||||
BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
|
||||
REMOTE_ORIGIN := $(shell git config --get remote.origin.url)
|
||||
|
||||
# * Handle multiple origin URL formats
|
||||
HTTPS_PREFIX_CHECK := $(shell echo $(REMOTE_ORIGIN) | head -c 8)
|
||||
HTTP_PREFIX_CHECK := $(shell echo $(REMOTE_ORIGIN) | head -c 7)
|
||||
GIT@_PREFIX_CHECK := $(shell echo $(REMOTE_ORIGIN) | head -c 4)
|
||||
|
||||
ifeq ($(HTTPS_PREFIX_CHECK), https://)
|
||||
REMOTE_PATH := $(shell echo $(REMOTE_ORIGIN) | cut -d/ -f4-)
|
||||
else ifeq ($(HTTP_PREFIX_CHECK), http://)
|
||||
REMOTE_PATH := $(shell echo $(REMOTE_ORIGIN) | cut -d/ -f4-)
|
||||
else ifeq ($(GIT@_PREFIX_CHECK), git@)
|
||||
REMOTE_PATH := $(shell echo $(REMOTE_ORIGIN) | cut -d: -f2-)
|
||||
else
|
||||
REMOTE_PATH := $(shell echo $(REMOTE_ORIGIN) | cut -d/ -f2-)
|
||||
endif
|
||||
|
||||
HASH := $(shell git rev-parse --short HEAD)
|
||||
SERVER_BUILD := $(BRANCH):$(REMOTE_PATH)@$(HASH)
|
||||
|
||||
else
|
||||
# * .git folder not present, assume downloaded from zip file and just use folder name
|
||||
$(info "$(CYAN)git repository not found. Building server build string from folder name$(RESET)")
|
||||
SERVER_BUILD := super-mario-maker
|
||||
endif
|
||||
|
||||
# * Final build string
|
||||
DATE_TIME := $(shell date --iso=seconds)
|
||||
BUILD_STRING := $(SERVER_BUILD), $(DATE_TIME)
|
||||
|
||||
default:
|
||||
ifeq ($(wildcard .env),)
|
||||
$(warning "$(YELLOW).env file not found, environment variables may not be populated correctly$(RESET)")
|
||||
endif
|
||||
go get -u
|
||||
go mod tidy
|
||||
go build -ldflags "-X 'main.serverBuildString=$(BUILD_STRING)'" -o ./build/super-mario-maker
|
||||
69
README.md
69
README.md
@@ -1,6 +1,65 @@
|
||||
# Super Mario Maker - Secure
|
||||
### Pretendo Super Mario Maker secure server
|
||||
# Super Mario Maker replacement server
|
||||
Includes both the authentication and secure servers. Works on both WiiU and 3DS
|
||||
|
||||
## About
|
||||
Only handles WiiU at this point. Only has enough implemented to get the Super Mario Maker app booting. Lots of things are not implemented
|
||||
Does not actually authenticate users atm!
|
||||
## DataStore (S3)
|
||||
This server requires an S3 compatible server to store user generated content. The WiiU and 3DS only support TLS versions 1.0 and 1.1 with RSA SSL ciphers, so an S3 server supporting these is required. The server must also support presigned `POST` URLs. This does not leave many options, as nearly all S3 cloud providers no longer support one of these 2 things. Even AWS, the creators of S3 and the provider Nintendo uses, is [dropping support for TLS versions below 1.2 in December 2023](https://aws.amazon.com/blogs/security/tls-1-2-required-for-aws-endpoints/)
|
||||
|
||||
For this reason, the recommended setup is using [MinIO](https://min.io/) to self host your S3 server and using [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) to act as your reverse proxy. Tunnels support TLS versions 1.0 and 1.1 with the required ciphers, essentially using it as a TLS proxy. This does come at some cost, and you now must manage data storage and security yourself through MinIO, but there are no other options at this time outside of self hosting
|
||||
|
||||
## Compiling
|
||||
|
||||
### Setup
|
||||
Install [Go](https://go.dev/doc/install) and [git](https://git-scm.com/downloads), then clone and enter the repository
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/PretendoNetwork/super-mario-maker
|
||||
$ cd super-mario-maker
|
||||
```
|
||||
|
||||
### Compiling using `go`
|
||||
To compile using Go, `go get` the required modules and then `go build` to your desired location. You may also want to tidy the go modules, though this is optional
|
||||
|
||||
```bash
|
||||
$ go get -u
|
||||
$ go mod tidy
|
||||
$ go build -o build/super-mario-maker
|
||||
```
|
||||
|
||||
The server is now built to `build/super-mario-maker`
|
||||
|
||||
When compiling with only Go, the authentication servers build string is not automatically set. This should not cause any issues with gameplay, but it means that the server build will not be visible in any packet dumps or logs a title may produce
|
||||
|
||||
To compile the servers with the authentication server build string, add `-ldflags "-X 'main.serverBuildString=BUILD_STRING_HERE'"` to the build command, or use `make` to compile the server
|
||||
|
||||
### Compiling using `make`
|
||||
Compiling using `make` will read the local `.git` directory to create a dynamic authentication server build string, based on your repositories remote origin and current commit
|
||||
|
||||
Install `make` either through your systems package manager or the [official download](https://www.gnu.org/software/make/). We provide a `default` rule which compiles [using `go`](#compiling-using-go)
|
||||
|
||||
To build using `go`
|
||||
|
||||
```bash
|
||||
$ make
|
||||
```
|
||||
|
||||
The server is now built to `build/super-mario-maker`
|
||||
|
||||
## Configuration
|
||||
All configuration options are handled via environment variables
|
||||
|
||||
`.env` files are supported
|
||||
|
||||
| Name | Description | Required |
|
||||
|-------------------------------------|-----------------------------------------------------------------------|-----------------------------------------------|
|
||||
| `PN_SMM_POSTGRES_URI` | Fully qualified URI to your Postgres server | Yes |
|
||||
| `PN_SMM_AUTHENTICATION_SERVER_PORT` | Port for the authentication server | Yes |
|
||||
| `PN_SMM_SECURE_SERVER_HOST` | Host name for the secure server | Yes |
|
||||
| `PN_SMM_SECURE_SERVER_PORT` | Port for the secure server | Yes |
|
||||
| `PN_SMM_KERBEROS_PASSWORD` | Password used as part of the internal server data in Kerberos tickets | No (Default password `password` will be used) |
|
||||
| `PN_SMM_CONFIG_S3_ENDPOINT` | S3 server endpoint | Yes |
|
||||
| `PN_SMM_CONFIG_S3_ACCESS_KEY` | S3 access key ID | Yes |
|
||||
| `PN_SMM_CONFIG_S3_ACCESS_SECRET` | S3 secret | Yes |
|
||||
| `PN_SMM_CONFIG_S3_BUCKET` | S3 bucket | Yes |
|
||||
| `PN_SMM_ACCOUNT_GRPC_HOST` | Host name for your account server gRPC service | Yes |
|
||||
| `PN_SMM_ACCOUNT_GRPC_PORT` | Port for your account server gRPC service | Yes |
|
||||
| `PN_SMM_ACCOUNT_GRPC_API_KEY` | API key for your account server gRPC service | No (Assumed to be an open gRPC API) |
|
||||
@@ -1,6 +0,0 @@
|
||||
package database
|
||||
|
||||
func ConnectAll() {
|
||||
connectMongo()
|
||||
connectCassandra()
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
)
|
||||
|
||||
var cluster *gocql.ClusterConfig
|
||||
var cassandraClusterSession *gocql.Session
|
||||
|
||||
func connectCassandra() {
|
||||
// Connect to Cassandra
|
||||
|
||||
var err error
|
||||
|
||||
cluster = gocql.NewCluster("127.0.0.1")
|
||||
cluster.Timeout = 30 * time.Second
|
||||
|
||||
createKeyspace("pretendo_smm")
|
||||
|
||||
cluster.Keyspace = "pretendo_smm"
|
||||
|
||||
cassandraClusterSession, err = cluster.CreateSession()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create tables if missing
|
||||
|
||||
if err := cassandraClusterSession.Query(`CREATE TABLE IF NOT EXISTS pretendo_smm.courses (
|
||||
data_id bigint PRIMARY KEY,
|
||||
playable boolean,
|
||||
owner_pid int,
|
||||
name text,
|
||||
size int,
|
||||
creation_date bigint,
|
||||
update_date bigint,
|
||||
world_record_first_pid int,
|
||||
world_record_pid int,
|
||||
world_record_creation_date bigint,
|
||||
world_record_update_date bigint,
|
||||
world_record int,
|
||||
meta_binary blob,
|
||||
flag int,
|
||||
extra_data list<text>,
|
||||
data_type smallint,
|
||||
period smallint
|
||||
)`).Exec(); err != nil {
|
||||
fmt.Println("pretendo_smm.courses")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cassandraClusterSession.Query(`CREATE TABLE IF NOT EXISTS pretendo_smm.ratings (
|
||||
data_id bigint PRIMARY KEY,
|
||||
stars counter,
|
||||
attempts counter,
|
||||
failures counter,
|
||||
completions counter
|
||||
)`).Exec(); err != nil {
|
||||
fmt.Println("pretendo_smm.ratings")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cassandraClusterSession.Query(`CREATE TABLE IF NOT EXISTS pretendo_smm.buffer_queues (
|
||||
id uuid PRIMARY KEY,
|
||||
data_id bigint,
|
||||
slot int,
|
||||
buffer blob
|
||||
)`).Exec(); err != nil {
|
||||
fmt.Println("pretendo_smm.buffer_queues")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cassandraClusterSession.Query(`CREATE TABLE IF NOT EXISTS pretendo_smm.generator_last_id (
|
||||
node_id int PRIMARY KEY,
|
||||
last_id int
|
||||
)`).Exec(); err != nil {
|
||||
fmt.Println("pretendo_smm.generator_last_id")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cassandraClusterSession.Query(`CREATE TABLE IF NOT EXISTS pretendo_smm.user_play_info (
|
||||
pid int PRIMARY KEY,
|
||||
starred_courses set<bigint>,
|
||||
played_courses set<bigint>
|
||||
)`).Exec(); err != nil {
|
||||
fmt.Println("pretendo_smm.user_play_info")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("Connected to Cassandra")
|
||||
}
|
||||
|
||||
// Adapted from gocql common_test.go
|
||||
func createKeyspace(keyspace string) {
|
||||
flagRF := flag.Int("rf", 1, "replication factor for pretendo_smm keyspace")
|
||||
|
||||
c := *cluster
|
||||
c.Keyspace = "system"
|
||||
c.Timeout = 30 * time.Second
|
||||
|
||||
s, err := c.CreateSession()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer s.Close()
|
||||
|
||||
if err := s.Query(fmt.Sprintf(`CREATE KEYSPACE IF NOT EXISTS %s
|
||||
WITH replication = {
|
||||
'class' : 'SimpleStrategy',
|
||||
'replication_factor' : %d
|
||||
}`, keyspace, *flagRF)).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
var mongoClient *mongo.Client
|
||||
var mongoContext context.Context
|
||||
var mongoDatabase *mongo.Database
|
||||
var mongoCollection *mongo.Collection
|
||||
|
||||
func connectMongo() {
|
||||
mongoClient, _ = mongo.NewClient(options.Client().ApplyURI(os.Getenv("MONGO_URI")))
|
||||
mongoContext, _ = context.WithTimeout(context.Background(), 10*time.Second)
|
||||
_ = mongoClient.Connect(mongoContext)
|
||||
|
||||
mongoDatabase = mongoClient.Database("pretendo")
|
||||
mongoCollection = mongoDatabase.Collection("pnids")
|
||||
}
|
||||
24
database/connect_postgres.go
Normal file
24
database/connect_postgres.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"os"
|
||||
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
var Postgres *sql.DB
|
||||
|
||||
func ConnectPostgres() {
|
||||
var err error
|
||||
|
||||
Postgres, err = sql.Open("postgres", os.Getenv("PN_SMM_POSTGRES_URI"))
|
||||
if err != nil {
|
||||
globals.Logger.Critical(err.Error())
|
||||
}
|
||||
|
||||
globals.Logger.Success("Connected to Postgres!")
|
||||
|
||||
initPostgres()
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func CreateDataStoreIDGeneratorRow(nodeID int) {
|
||||
if err := cassandraClusterSession.Query(`INSERT INTO pretendo_smm.generator_last_id(node_id, last_id) VALUES (?, ?) IF NOT EXISTS`, nodeID, 0).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
23
database/datastore/delete_object_by_data_id.go
Normal file
23
database/datastore/delete_object_by_data_id.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func DeleteObjectByDataID(dataID uint64) uint32 {
|
||||
errCode := IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
_, err := database.Postgres.Exec(`UPDATE datastore.objects SET deleted=TRUE WHERE data_id=$1`, dataID)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
91
database/datastore/get_object_info_by_data_id.go
Normal file
91
database/datastore/get_object_info_by_data_id.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func GetObjectInfoByDataID(dataID uint64) (*datastore_types.DataStoreMetaInfo, uint32) {
|
||||
errCode := IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
metaInfo := datastore_types.NewDataStoreMetaInfo()
|
||||
metaInfo.Permission = datastore_types.NewDataStorePermission()
|
||||
metaInfo.DelPermission = datastore_types.NewDataStorePermission()
|
||||
metaInfo.CreatedTime = nex.NewDateTime(0)
|
||||
metaInfo.UpdatedTime = nex.NewDateTime(0)
|
||||
metaInfo.ReferredTime = nex.NewDateTime(0)
|
||||
metaInfo.ExpireTime = nex.NewDateTime(0x9C3f3E0000) // * 9999-12-31T00:00:00.000Z. This is what the real server sends
|
||||
metaInfo.Ratings = make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
|
||||
var createdDate time.Time
|
||||
var updatedDate time.Time
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT
|
||||
data_id,
|
||||
owner,
|
||||
size,
|
||||
name,
|
||||
data_type,
|
||||
meta_binary,
|
||||
permission,
|
||||
permission_recipients,
|
||||
delete_permission,
|
||||
delete_permission_recipients,
|
||||
period,
|
||||
refer_data_id,
|
||||
flag,
|
||||
tags,
|
||||
creation_date,
|
||||
update_date
|
||||
FROM datastore.objects WHERE data_id=$1`, dataID).Scan(
|
||||
&metaInfo.DataID,
|
||||
&metaInfo.OwnerID,
|
||||
&metaInfo.Size,
|
||||
&metaInfo.Name,
|
||||
&metaInfo.DataType,
|
||||
&metaInfo.MetaBinary,
|
||||
&metaInfo.Permission.Permission,
|
||||
pq.Array(&metaInfo.Permission.RecipientIDs),
|
||||
&metaInfo.DelPermission.Permission,
|
||||
pq.Array(&metaInfo.DelPermission.RecipientIDs),
|
||||
&metaInfo.Period,
|
||||
&metaInfo.ReferDataID,
|
||||
&metaInfo.Flag,
|
||||
pq.Array(&metaInfo.Tags),
|
||||
&createdDate,
|
||||
&updatedDate,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
ratings, errCode := GetObjectRatingsWithSlotByDataID(metaInfo.DataID)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
metaInfo.Ratings = ratings
|
||||
|
||||
metaInfo.CreatedTime.FromTimestamp(createdDate)
|
||||
metaInfo.UpdatedTime.FromTimestamp(updatedDate)
|
||||
metaInfo.ReferredTime.FromTimestamp(createdDate) // * This is what the real server does
|
||||
|
||||
return metaInfo, 0
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func GetObjectInfoByDataIDWithPassword(dataID uint64, password uint64) (*datastore_types.DataStoreMetaInfo, uint32) {
|
||||
errCode := IsObjectAvailableWithPassword(dataID, password)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
metaInfo := datastore_types.NewDataStoreMetaInfo()
|
||||
metaInfo.Permission = datastore_types.NewDataStorePermission()
|
||||
metaInfo.DelPermission = datastore_types.NewDataStorePermission()
|
||||
metaInfo.CreatedTime = nex.NewDateTime(0)
|
||||
metaInfo.UpdatedTime = nex.NewDateTime(0)
|
||||
metaInfo.ReferredTime = nex.NewDateTime(0)
|
||||
metaInfo.ExpireTime = nex.NewDateTime(0x9C3f3E0000) // * 9999-12-31T00:00:00.000Z. This is what the real server sends
|
||||
metaInfo.Ratings = make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
|
||||
var createdDate time.Time
|
||||
var updatedDate time.Time
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT
|
||||
data_id,
|
||||
owner,
|
||||
size,
|
||||
name,
|
||||
data_type,
|
||||
meta_binary,
|
||||
permission,
|
||||
permission_recipients,
|
||||
delete_permission,
|
||||
delete_permission_recipients,
|
||||
period,
|
||||
refer_data_id,
|
||||
flag,
|
||||
tags,
|
||||
creation_date,
|
||||
update_date
|
||||
FROM datastore.objects WHERE data_id=$1`, dataID).Scan(
|
||||
&metaInfo.DataID,
|
||||
&metaInfo.OwnerID,
|
||||
&metaInfo.Size,
|
||||
&metaInfo.Name,
|
||||
&metaInfo.DataType,
|
||||
&metaInfo.MetaBinary,
|
||||
&metaInfo.Permission.Permission,
|
||||
pq.Array(&metaInfo.Permission.RecipientIDs),
|
||||
&metaInfo.DelPermission.Permission,
|
||||
pq.Array(&metaInfo.DelPermission.RecipientIDs),
|
||||
&metaInfo.Period,
|
||||
&metaInfo.ReferDataID,
|
||||
&metaInfo.Flag,
|
||||
pq.Array(&metaInfo.Tags),
|
||||
&createdDate,
|
||||
&updatedDate,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
ratings, errCode := GetObjectRatingsWithSlotByDataIDWithPassword(metaInfo.DataID, password)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
metaInfo.Ratings = ratings
|
||||
|
||||
metaInfo.CreatedTime.FromTimestamp(createdDate)
|
||||
metaInfo.UpdatedTime.FromTimestamp(updatedDate)
|
||||
metaInfo.ReferredTime.FromTimestamp(createdDate) // * This is what the real server does
|
||||
|
||||
return metaInfo, 0
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func GetObjectInfoByPersistenceTargetWithPassword(persistenceTarget *datastore_types.DataStorePersistenceTarget, password uint64) (*datastore_types.DataStoreMetaInfo, uint32) {
|
||||
metaInfo := datastore_types.NewDataStoreMetaInfo()
|
||||
metaInfo.Permission = datastore_types.NewDataStorePermission()
|
||||
metaInfo.DelPermission = datastore_types.NewDataStorePermission()
|
||||
metaInfo.CreatedTime = nex.NewDateTime(0)
|
||||
metaInfo.UpdatedTime = nex.NewDateTime(0)
|
||||
metaInfo.ReferredTime = nex.NewDateTime(0)
|
||||
metaInfo.ExpireTime = nex.NewDateTime(0x9C3f3E0000) // * 9999-12-31T00:00:00.000Z. This is what the real server sends
|
||||
metaInfo.Ratings = make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
|
||||
var accessPassword uint64
|
||||
var underReview bool
|
||||
var createdDate time.Time
|
||||
var updatedDate time.Time
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT
|
||||
data_id,
|
||||
owner,
|
||||
size,
|
||||
name,
|
||||
data_type,
|
||||
meta_binary,
|
||||
permission,
|
||||
permission_recipients,
|
||||
delete_permission,
|
||||
delete_permission_recipients,
|
||||
period,
|
||||
refer_data_id,
|
||||
flag,
|
||||
tags,
|
||||
creation_date,
|
||||
update_date,
|
||||
access_password,
|
||||
under_review
|
||||
FROM datastore.objects WHERE owner=$1 AND persistence_slot_id=$2 AND upload_completed=TRUE AND deleted=FALSE`, persistenceTarget.OwnerID, persistenceTarget.PersistenceSlotID).Scan(
|
||||
&metaInfo.DataID,
|
||||
&metaInfo.OwnerID,
|
||||
&metaInfo.Size,
|
||||
&metaInfo.Name,
|
||||
&metaInfo.DataType,
|
||||
&metaInfo.MetaBinary,
|
||||
&metaInfo.Permission.Permission,
|
||||
pq.Array(&metaInfo.Permission.RecipientIDs),
|
||||
&metaInfo.DelPermission.Permission,
|
||||
pq.Array(&metaInfo.DelPermission.RecipientIDs),
|
||||
&metaInfo.Period,
|
||||
&metaInfo.ReferDataID,
|
||||
&metaInfo.Flag,
|
||||
pq.Array(&metaInfo.Tags),
|
||||
&createdDate,
|
||||
&updatedDate,
|
||||
&accessPassword,
|
||||
&underReview,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
if accessPassword != 0 && accessPassword != password {
|
||||
return nil, nex.Errors.DataStore.InvalidPassword
|
||||
}
|
||||
|
||||
if underReview {
|
||||
return nil, nex.Errors.DataStore.UnderReviewing
|
||||
}
|
||||
|
||||
ratings, errCode := GetObjectRatingsWithSlotByDataIDWithPassword(metaInfo.DataID, password)
|
||||
if errCode != 0 {
|
||||
globals.Logger.Errorf("Failed to get ratings for object %d with password %d", metaInfo.DataID, password)
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
metaInfo.Ratings = ratings
|
||||
|
||||
metaInfo.CreatedTime.FromTimestamp(createdDate)
|
||||
metaInfo.UpdatedTime.FromTimestamp(updatedDate)
|
||||
metaInfo.ReferredTime.FromTimestamp(createdDate) // * This is what the real server does
|
||||
|
||||
return metaInfo, 0
|
||||
}
|
||||
26
database/datastore/get_object_owner_by_data_id.go
Normal file
26
database/datastore/get_object_owner_by_data_id.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetObjectOwnerByDataID(dataID uint64) (uint32, uint32) {
|
||||
var owner uint32
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT owner FROM datastore.objects WHERE data_id=$1`, dataID).Scan(&owner)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return 0, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return owner, 0
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetObjectRatingsWithSlotByDataID(dataID uint64) ([]*datastore_types.DataStoreRatingInfoWithSlot, uint32) {
|
||||
errCode := IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
ratings := make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
|
||||
rows, err := database.Postgres.Query(`SELECT slot, total_value, count, initial_value FROM datastore.object_ratings WHERE data_id=$1`, dataID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
rating := datastore_types.NewDataStoreRatingInfoWithSlot()
|
||||
rating.Rating = datastore_types.NewDataStoreRatingInfo()
|
||||
|
||||
err := rows.Scan(&rating.Slot, &rating.Rating.TotalValue, &rating.Rating.Count, &rating.Rating.InitialValue)
|
||||
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
ratings = append(ratings, rating)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return ratings, 0
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetObjectRatingsWithSlotByDataIDWithPassword(dataID uint64, password uint64) ([]*datastore_types.DataStoreRatingInfoWithSlot, uint32) {
|
||||
errCode := IsObjectAvailableWithPassword(dataID, password)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
ratings := make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
|
||||
rows, err := database.Postgres.Query(`SELECT slot, total_value, count, initial_value FROM datastore.object_ratings WHERE data_id=$1`, dataID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
rating := datastore_types.NewDataStoreRatingInfoWithSlot()
|
||||
rating.Rating = datastore_types.NewDataStoreRatingInfo()
|
||||
|
||||
err := rows.Scan(&rating.Slot, &rating.Rating.TotalValue, &rating.Rating.Count, &rating.Rating.InitialValue)
|
||||
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
ratings = append(ratings, rating)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return ratings, 0
|
||||
}
|
||||
26
database/datastore/get_object_size_by_data_id.go
Normal file
26
database/datastore/get_object_size_by_data_id.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetObjectSizeDataID(dataID uint64) (uint32, uint32) {
|
||||
var size uint32
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT size FROM datastore.objects WHERE data_id=$1`, dataID).Scan(&size)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return 0, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return size, 0
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func InitializeObjectByPreparePostParam(ownerPID uint32, param *datastore_types.DataStorePreparePostParam) (uint64, uint32) {
|
||||
now := time.Now()
|
||||
|
||||
var dataID uint64
|
||||
|
||||
err := database.Postgres.QueryRow(`INSERT INTO datastore.objects (
|
||||
owner,
|
||||
size,
|
||||
name,
|
||||
data_type,
|
||||
meta_binary,
|
||||
permission,
|
||||
permission_recipients,
|
||||
delete_permission,
|
||||
delete_permission_recipients,
|
||||
flag,
|
||||
period,
|
||||
refer_data_id,
|
||||
tags,
|
||||
persistence_slot_id,
|
||||
extra_data,
|
||||
creation_date,
|
||||
update_date
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9,
|
||||
$10,
|
||||
$11,
|
||||
$12,
|
||||
$13,
|
||||
$14,
|
||||
$15,
|
||||
$16,
|
||||
$17
|
||||
) RETURNING data_id`,
|
||||
ownerPID,
|
||||
param.Size,
|
||||
param.Name,
|
||||
param.DataType,
|
||||
param.MetaBinary,
|
||||
param.Permission.Permission,
|
||||
pq.Array(param.Permission.RecipientIDs),
|
||||
param.DelPermission.Permission,
|
||||
pq.Array(param.DelPermission.RecipientIDs),
|
||||
param.Flag,
|
||||
param.Period,
|
||||
param.ReferDataID,
|
||||
pq.Array(param.Tags),
|
||||
param.PersistenceInitParam.PersistenceSlotID, // TODO - Check param.PersistenceInitParam.DeleteLastObject?
|
||||
pq.Array(param.ExtraData),
|
||||
now,
|
||||
now,
|
||||
).Scan(&dataID)
|
||||
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return 0, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return dataID, 0
|
||||
}
|
||||
56
database/datastore/initialize_object_rating_with_slot.go
Normal file
56
database/datastore/initialize_object_rating_with_slot.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func InitializeObjectRatingWithSlot(dataID uint64, param *datastore_types.DataStoreRatingInitParamWithSlot) uint32 {
|
||||
_, err := database.Postgres.Exec(`INSERT INTO datastore.object_ratings (
|
||||
data_id,
|
||||
slot,
|
||||
flag,
|
||||
internal_flag,
|
||||
lock_type,
|
||||
initial_value,
|
||||
range_min,
|
||||
range_max,
|
||||
period_hour,
|
||||
period_duration,
|
||||
total_value
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9,
|
||||
$10,
|
||||
$11
|
||||
)`,
|
||||
dataID,
|
||||
param.Slot,
|
||||
param.Param.Flag,
|
||||
param.Param.InternalFlag,
|
||||
param.Param.LockType,
|
||||
param.Param.InitialValue,
|
||||
param.Param.RangeMin,
|
||||
param.Param.RangeMax,
|
||||
param.Param.PeriodHour,
|
||||
param.Param.PeriodDuration,
|
||||
param.Param.InitialValue, // * Start the value off at the initial value
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
30
database/datastore/is_object_available.go
Normal file
30
database/datastore/is_object_available.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func IsObjectAvailable(dataID uint64) uint32 {
|
||||
var underReview bool
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT under_review FROM datastore.objects WHERE data_id=$1 AND upload_completed=TRUE AND deleted=FALSE`, dataID).Scan(&underReview)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
if underReview {
|
||||
return nex.Errors.DataStore.UnderReviewing
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
42
database/datastore/is_object_available_with_password.go
Normal file
42
database/datastore/is_object_available_with_password.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func IsObjectAvailableWithPassword(dataID, password uint64) uint32 {
|
||||
var underReview bool
|
||||
var accessPassword uint64
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT
|
||||
under_review,
|
||||
access_password
|
||||
FROM datastore.objects WHERE data_id=$1 AND upload_completed=TRUE AND deleted=FALSE`, dataID).Scan(
|
||||
&underReview,
|
||||
&accessPassword,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
if accessPassword != 0 && accessPassword != password {
|
||||
return nex.Errors.DataStore.InvalidPassword
|
||||
}
|
||||
|
||||
if underReview {
|
||||
return nex.Errors.DataStore.UnderReviewing
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
46
database/datastore/rate_object_with_password.go
Normal file
46
database/datastore/rate_object_with_password.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func RateObjectWithPassword(dataID uint64, slot uint8, ratingValue int32, accessPassword uint64) (*datastore_types.DataStoreRatingInfo, uint32) {
|
||||
errCode := IsObjectAvailableWithPassword(dataID, accessPassword)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
rating := datastore_types.NewDataStoreRatingInfo()
|
||||
|
||||
err := database.Postgres.QueryRow(`
|
||||
UPDATE datastore.object_ratings
|
||||
SET total_value=total_value+$1, count=count+1
|
||||
WHERE data_id=$2 AND slot=$3
|
||||
RETURNING total_value, count, initial_value`, ratingValue, dataID, slot,
|
||||
).Scan(
|
||||
&rating.TotalValue,
|
||||
&rating.Count,
|
||||
&rating.InitialValue,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// * If no data was returned, that means the slot
|
||||
// * was out of bounds since the object exists due
|
||||
// * to the check at the start of the function.
|
||||
// * This is an invalid argument
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.Errors.DataStore.InvalidArgument
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return rating, 0
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
)
|
||||
|
||||
func GetBufferQueuesByDataIDAndSlot(dataID uint64, slot uint32) ([][]byte, uint32) {
|
||||
errCode := datastore_db.IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
bufferQueues := make([][]byte, 0)
|
||||
|
||||
rows, err := database.Postgres.Query(`SELECT buffer FROM datastore.buffer_queues WHERE data_id=$1 AND slot=$2 ORDER BY creation_date`, dataID, slot)
|
||||
|
||||
// * No rows is allowed
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var buffer []byte
|
||||
|
||||
err := rows.Scan(&buffer)
|
||||
if err != nil {
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
bufferQueues = append(bufferQueues, buffer)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return bufferQueues, 0
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetCourseRecordByDataIDAndSlot(dataID uint64, slot uint8) (*datastore_super_mario_maker_types.DataStoreGetCourseRecordResult, uint32) {
|
||||
errCode := datastore_db.IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
courseRecord := datastore_super_mario_maker_types.NewDataStoreGetCourseRecordResult()
|
||||
courseRecord.DataID = dataID
|
||||
courseRecord.Slot = slot
|
||||
courseRecord.CreatedTime = nex.NewDateTime(0)
|
||||
courseRecord.UpdatedTime = nex.NewDateTime(0)
|
||||
|
||||
var createdDate time.Time
|
||||
var updatedDate time.Time
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT
|
||||
first_pid,
|
||||
best_pid,
|
||||
best_score,
|
||||
creation_date,
|
||||
update_date
|
||||
FROM datastore.course_records WHERE data_id=$1 AND slot=$2`,
|
||||
dataID,
|
||||
slot,
|
||||
).Scan(
|
||||
&courseRecord.FirstPID,
|
||||
&courseRecord.BestPID,
|
||||
&courseRecord.BestScore,
|
||||
&createdDate,
|
||||
&updatedDate,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
courseRecord.CreatedTime.FromTimestamp(createdDate)
|
||||
courseRecord.UpdatedTime.FromTimestamp(updatedDate)
|
||||
|
||||
return courseRecord, 0
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func GetCustomRankingsByDataIDs(applicationID uint32, dataIDs []uint64) []*datastore_super_mario_maker_types.DataStoreCustomRankingResult {
|
||||
// * Unlike other methods which query for multiple objects,
|
||||
// * DataStoreSMM::GetCustomRankingByDataID *OMITS* objects
|
||||
// * from it's response if they could not be found, rather
|
||||
// * than using a zero-ed object and DataStore::NotFound as
|
||||
// * the Result. Because of this, all errors are just thrown
|
||||
// * away here and not sent back to the client
|
||||
results := make([]*datastore_super_mario_maker_types.DataStoreCustomRankingResult, 0, len(dataIDs))
|
||||
|
||||
// * Using UNNEST and WITH ORDINALITY because the input
|
||||
// * array may contain duplicate DataIDs. These duplicate
|
||||
// * DataIDs should result in duplicate results, and this
|
||||
// * was the most efficient way to do this as it doesn't
|
||||
// * involve processing all rows or manually duplicating
|
||||
// * rows
|
||||
rows, err := database.Postgres.Query(`
|
||||
SELECT
|
||||
rankings.data_id,
|
||||
rankings.value
|
||||
FROM datastore.object_custom_rankings rankings
|
||||
JOIN UNNEST($1::bigint[])
|
||||
WITH ORDINALITY AS rows(data_id, ord)
|
||||
ON rankings.data_id = rows.data_id
|
||||
AND rankings.application_id = $2
|
||||
ORDER BY rows.ord`,
|
||||
pq.Array(dataIDs),
|
||||
applicationID,
|
||||
)
|
||||
|
||||
// * No rows is allowed
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
globals.Logger.Error(err.Error())
|
||||
return results
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var dataID uint64
|
||||
var value uint32
|
||||
|
||||
err := rows.Scan(&dataID, &value)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
objectInfo, errCode := datastore_db.GetObjectInfoByDataID(dataID)
|
||||
if errCode != 0 {
|
||||
globals.Logger.Errorf("Got error code %d for object %d", errCode, dataID)
|
||||
continue
|
||||
}
|
||||
|
||||
result := datastore_super_mario_maker_types.NewDataStoreCustomRankingResult()
|
||||
|
||||
// * Order is always 0, for some reason
|
||||
result.Score = value
|
||||
result.MetaInfo = objectInfo
|
||||
|
||||
results = append(results, result)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func GetRandomCoursesWithLimit(limit int) ([]*datastore_super_mario_maker_types.DataStoreCustomRankingResult, uint32) {
|
||||
courses := make([]*datastore_super_mario_maker_types.DataStoreCustomRankingResult, 0)
|
||||
|
||||
rows, err := database.Postgres.Query(`
|
||||
SELECT
|
||||
object.data_id,
|
||||
object.owner,
|
||||
object.size,
|
||||
object.name,
|
||||
object.data_type,
|
||||
object.meta_binary,
|
||||
object.permission,
|
||||
object.permission_recipients,
|
||||
object.delete_permission,
|
||||
object.delete_permission_recipients,
|
||||
object.period,
|
||||
object.refer_data_id,
|
||||
object.flag,
|
||||
object.tags,
|
||||
object.creation_date,
|
||||
object.update_date,
|
||||
ranking.value
|
||||
FROM datastore.objects object
|
||||
JOIN datastore.object_custom_rankings ranking
|
||||
ON
|
||||
object.data_id = ranking.data_id AND
|
||||
object.upload_completed = TRUE AND
|
||||
object.deleted = FALSE AND
|
||||
object.under_review = FALSE AND
|
||||
ranking.application_id = 0
|
||||
ORDER BY RANDOM()
|
||||
LIMIT $1
|
||||
`, limit)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
course := datastore_super_mario_maker_types.NewDataStoreCustomRankingResult()
|
||||
course.Order = 0 // * Order is ALWAYS 0
|
||||
course.MetaInfo = datastore_types.NewDataStoreMetaInfo()
|
||||
course.MetaInfo.Permission = datastore_types.NewDataStorePermission()
|
||||
course.MetaInfo.DelPermission = datastore_types.NewDataStorePermission()
|
||||
course.MetaInfo.CreatedTime = nex.NewDateTime(0)
|
||||
course.MetaInfo.UpdatedTime = nex.NewDateTime(0)
|
||||
course.MetaInfo.ReferredTime = nex.NewDateTime(0)
|
||||
course.MetaInfo.ExpireTime = nex.NewDateTime(0x9C3f3E0000) // * 9999-12-31T00:00:00.000Z. This is what the real server sends
|
||||
course.MetaInfo.Ratings = make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
|
||||
var createdDate time.Time
|
||||
var updatedDate time.Time
|
||||
|
||||
err := rows.Scan(
|
||||
&course.MetaInfo.DataID,
|
||||
&course.MetaInfo.OwnerID,
|
||||
&course.MetaInfo.Size,
|
||||
&course.MetaInfo.Name,
|
||||
&course.MetaInfo.DataType,
|
||||
&course.MetaInfo.MetaBinary,
|
||||
&course.MetaInfo.Permission.Permission,
|
||||
pq.Array(&course.MetaInfo.Permission.RecipientIDs),
|
||||
&course.MetaInfo.DelPermission.Permission,
|
||||
pq.Array(&course.MetaInfo.DelPermission.RecipientIDs),
|
||||
&course.MetaInfo.Period,
|
||||
&course.MetaInfo.ReferDataID,
|
||||
&course.MetaInfo.Flag,
|
||||
pq.Array(&course.MetaInfo.Tags),
|
||||
&createdDate,
|
||||
&updatedDate,
|
||||
&course.Score,
|
||||
)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
ratings, errCode := datastore_db.GetObjectRatingsWithSlotByDataID(course.MetaInfo.DataID)
|
||||
if errCode != 0 {
|
||||
return nil, errCode
|
||||
}
|
||||
|
||||
course.MetaInfo.Ratings = ratings
|
||||
|
||||
course.MetaInfo.CreatedTime.FromTimestamp(createdDate)
|
||||
course.MetaInfo.UpdatedTime.FromTimestamp(updatedDate)
|
||||
course.MetaInfo.ReferredTime.FromTimestamp(createdDate) // * This is what the real server does
|
||||
|
||||
courses = append(courses, course)
|
||||
}
|
||||
|
||||
return courses, 0
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetUserCourseObjectIDs(ownerPID uint32) ([]uint64, uint32) {
|
||||
courseObjectIDs := make([]uint64, 0)
|
||||
|
||||
// * Course objects seem to have data types > 2 and < 50.
|
||||
// * Data type 1 seems to be reserved for "maker" objects.
|
||||
// * Data type 2 seems to be reserved for objects
|
||||
// * created through "PrepareAttachFile".
|
||||
// * Data type 50 is reserved for the Event Courses metadata
|
||||
// * file, and data type 51 is reserved for event courses
|
||||
rows, err := database.Postgres.Query(`SELECT data_id FROM datastore.objects WHERE owner=$1 AND data_type > 2 AND data_type < 50`, ownerPID)
|
||||
|
||||
// * No rows is allowed
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var dataID uint64
|
||||
|
||||
err := rows.Scan(&dataID)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
errCode := datastore_db.IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
courseObjectIDs = append(courseObjectIDs, dataID)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
// TODO - Send more specific errors?
|
||||
return nil, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return courseObjectIDs, 0
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_smm_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func InitializeObjectByAttachFileParam(ownerPID uint32, param *datastore_smm_types.DataStoreAttachFileParam) (uint64, uint32) {
|
||||
now := time.Now()
|
||||
|
||||
var dataID uint64
|
||||
|
||||
err := database.Postgres.QueryRow(`INSERT INTO datastore.objects (
|
||||
owner,
|
||||
size,
|
||||
name,
|
||||
data_type,
|
||||
meta_binary,
|
||||
permission,
|
||||
permission_recipients,
|
||||
delete_permission,
|
||||
delete_permission_recipients,
|
||||
flag,
|
||||
period,
|
||||
refer_data_id,
|
||||
tags,
|
||||
persistence_slot_id,
|
||||
extra_data,
|
||||
creation_date,
|
||||
update_date
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9,
|
||||
$10,
|
||||
$11,
|
||||
$12,
|
||||
$13,
|
||||
$14,
|
||||
$15,
|
||||
$16,
|
||||
$17
|
||||
) RETURNING data_id`,
|
||||
ownerPID,
|
||||
param.PostParam.Size,
|
||||
param.PostParam.Name,
|
||||
param.PostParam.DataType,
|
||||
param.PostParam.MetaBinary,
|
||||
param.PostParam.Permission.Permission,
|
||||
pq.Array(param.PostParam.Permission.RecipientIDs),
|
||||
param.PostParam.DelPermission.Permission,
|
||||
pq.Array(param.PostParam.DelPermission.RecipientIDs),
|
||||
param.PostParam.Flag,
|
||||
param.PostParam.Period,
|
||||
param.ReferDataID,
|
||||
pq.Array(param.PostParam.Tags),
|
||||
param.PostParam.PersistenceInitParam.PersistenceSlotID, // TODO - Check param.PersistenceInitParam.DeleteLastObject?
|
||||
pq.Array(param.PostParam.ExtraData),
|
||||
now,
|
||||
now,
|
||||
).Scan(&dataID)
|
||||
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return 0, nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return dataID, 0
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
)
|
||||
|
||||
func InsertOrUpdateBufferQueueData(dataID uint64, slot uint32, buffer []byte) uint32 {
|
||||
errCode := datastore_db.IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// * Real server does not allow duplicate
|
||||
// * buffers to be in a given objects slot,
|
||||
// * even if the buffers were uploaded by
|
||||
// * different clients. Instead, it removes
|
||||
// * the older buffer and adds the newer one.
|
||||
// * Instead of that, we just update the
|
||||
// * creation time
|
||||
_, err := database.Postgres.Exec(`INSERT INTO datastore.buffer_queues (
|
||||
data_id,
|
||||
slot,
|
||||
creation_date,
|
||||
buffer
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4
|
||||
) ON CONFLICT (data_id, slot, buffer) DO UPDATE SET creation_date=$3`, dataID, slot, now, buffer)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func InsertOrUpdateCourseRecord(dataID uint64, slot uint8, pid uint32, score int32) uint32 {
|
||||
errCode := datastore_db.IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
globals.Logger.Errorf("Error code %d", errCode)
|
||||
return errCode
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
_, err := database.Postgres.Exec(`INSERT INTO datastore.course_records (
|
||||
data_id,
|
||||
slot,
|
||||
first_pid,
|
||||
best_pid,
|
||||
best_score,
|
||||
creation_date,
|
||||
update_date
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
) ON CONFLICT (data_id, slot) DO UPDATE
|
||||
SET best_score = CASE WHEN datastore.course_records.best_score > $5 THEN $5 ELSE datastore.course_records.best_score END,
|
||||
best_pid = CASE WHEN datastore.course_records.best_score > $5 THEN $4 ELSE datastore.course_records.best_pid END,
|
||||
update_date = CASE WHEN datastore.course_records.best_score > $5 THEN $7 ELSE datastore.course_records.update_date END`,
|
||||
dataID,
|
||||
slot,
|
||||
pid,
|
||||
pid,
|
||||
score,
|
||||
now,
|
||||
now,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package datastore_smm_db
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func InsertOrUpdateCustomRanking(dataID uint64, applicationID, score uint32) uint32 {
|
||||
errCode := datastore_db.IsObjectAvailable(dataID)
|
||||
if errCode != 0 {
|
||||
globals.Logger.Errorf("Error code %d", errCode)
|
||||
return errCode
|
||||
}
|
||||
|
||||
_, err := database.Postgres.Exec(`INSERT INTO datastore.object_custom_rankings (
|
||||
data_id,
|
||||
application_id,
|
||||
value
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3
|
||||
) ON CONFLICT (data_id, application_id) DO UPDATE SET value=datastore.object_custom_rankings.value+EXCLUDED.value`,
|
||||
dataID,
|
||||
applicationID,
|
||||
score,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func UpdateObjectDataTypeByDataIDWithPassword(dataID uint64, dataType uint16, password uint64) uint32 {
|
||||
var updatePassword uint64
|
||||
var underReview bool
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT update_password, under_review FROM datastore.objects WHERE data_id=$1 AND upload_completed=TRUE AND deleted=FALSE`, dataID).Scan(
|
||||
&updatePassword,
|
||||
&underReview,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
if updatePassword != 0 && updatePassword != password {
|
||||
return nex.Errors.DataStore.InvalidPassword
|
||||
}
|
||||
|
||||
if underReview {
|
||||
return nex.Errors.DataStore.UnderReviewing
|
||||
}
|
||||
|
||||
_, err = database.Postgres.Exec(`UPDATE datastore.objects SET data_type=$1 WHERE data_id=$2`, dataType, dataID)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func UpdateObjectMetaBinaryByDataIDWithPassword(dataID uint64, metaBinary []byte, password uint64) uint32 {
|
||||
var updatePassword uint64
|
||||
var underReview bool
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT update_password, under_review FROM datastore.objects WHERE data_id=$1 AND upload_completed=TRUE AND deleted=FALSE`, dataID).Scan(
|
||||
&updatePassword,
|
||||
&underReview,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
if updatePassword != 0 && updatePassword != password {
|
||||
return nex.Errors.DataStore.InvalidPassword
|
||||
}
|
||||
|
||||
if underReview {
|
||||
return nex.Errors.DataStore.UnderReviewing
|
||||
}
|
||||
|
||||
_, err = database.Postgres.Exec(`UPDATE datastore.objects SET meta_binary=$1 WHERE data_id=$2`, metaBinary, dataID)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func UpdateObjectPeriodByDataIDWithPassword(dataID uint64, period uint16, password uint64) uint32 {
|
||||
var updatePassword uint64
|
||||
var underReview bool
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT update_password, under_review FROM datastore.objects WHERE data_id=$1 AND upload_completed=TRUE AND deleted=FALSE`, dataID).Scan(
|
||||
&updatePassword,
|
||||
&underReview,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
if updatePassword != 0 && updatePassword != password {
|
||||
return nex.Errors.DataStore.InvalidPassword
|
||||
}
|
||||
|
||||
if underReview {
|
||||
return nex.Errors.DataStore.UnderReviewing
|
||||
}
|
||||
|
||||
_, err = database.Postgres.Exec(`UPDATE datastore.objects SET period=$1 WHERE data_id=$2`, period, dataID)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package datastore_db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func UpdateObjectUploadCompletedByDataID(dataID uint64, uploadCompleted bool) uint32 {
|
||||
var underReview bool
|
||||
|
||||
err := database.Postgres.QueryRow(`SELECT update_password FROM datastore.objects WHERE data_id=$1 AND deleted=FALSE`, dataID).Scan(&underReview)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
globals.Logger.Error(err.Error())
|
||||
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
if underReview {
|
||||
return nex.Errors.DataStore.UnderReviewing
|
||||
}
|
||||
|
||||
_, err = database.Postgres.Exec(`UPDATE datastore.objects SET upload_completed=$1 WHERE data_id=$2`, uploadCompleted, dataID)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
// TODO - Send more specific errors?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// DataStoreIDGenerator represents a safe, structured, ID for unique NEX DataStore objects
|
||||
type DataStoreIDGenerator struct {
|
||||
inUse bool
|
||||
regionID uint8
|
||||
nodeID uint8
|
||||
Value uint32
|
||||
}
|
||||
|
||||
// InUse is used to check if a DataStoreIDGenerator Node is currently generating a number
|
||||
func (dataStoreIDGenerator *DataStoreIDGenerator) InUse() bool {
|
||||
return dataStoreIDGenerator.inUse
|
||||
}
|
||||
|
||||
// SetInUse is used to check if a DataStoreIDGenerator Node is currently generating a number
|
||||
func (dataStoreIDGenerator *DataStoreIDGenerator) SetInUse(inUse bool) {
|
||||
dataStoreIDGenerator.inUse = inUse
|
||||
}
|
||||
|
||||
// Next is used to get the next value from the DataStoreIDGenerator Node
|
||||
func (dataStoreIDGenerator *DataStoreIDGenerator) Next() uint64 {
|
||||
if dataStoreIDGenerator.inUse {
|
||||
panic("Cannot call DataStoreIDGenerator.Next while in use")
|
||||
}
|
||||
|
||||
dataStoreIDGenerator.Value += 1
|
||||
|
||||
data := make([]byte, 8)
|
||||
|
||||
data[2] = dataStoreIDGenerator.regionID
|
||||
data[3] = dataStoreIDGenerator.nodeID
|
||||
binary.BigEndian.PutUint32(data[4:], dataStoreIDGenerator.Value)
|
||||
|
||||
return binary.BigEndian.Uint64(data)
|
||||
}
|
||||
|
||||
func NewDataStoreIDGenerator(regionID, nodeID uint8, value uint32) *DataStoreIDGenerator {
|
||||
return &DataStoreIDGenerator{
|
||||
regionID: regionID,
|
||||
nodeID: nodeID,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func GetBufferQueueDeathData(dataID uint64) [][]byte {
|
||||
pBufferQueue := make([][]byte, 0)
|
||||
|
||||
var sliceMap []map[string]interface{}
|
||||
var err error
|
||||
|
||||
if sliceMap, err = cassandraClusterSession.Query(`SELECT buffer FROM pretendo_smm.buffer_queues WHERE data_id=? AND slot=3 ALLOW FILTERING`, dataID).Iter().SliceMap(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, bufferQueue := range sliceMap {
|
||||
pBufferQueue = append(pBufferQueue, bufferQueue["buffer"].([]byte))
|
||||
}
|
||||
|
||||
return pBufferQueue
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/types"
|
||||
"github.com/gocql/gocql"
|
||||
)
|
||||
|
||||
func GetCourseMetadataByDataID(dataID uint64) *types.CourseMetadata {
|
||||
var ownerPID uint32
|
||||
var size uint32
|
||||
var name string
|
||||
var metaBinary []byte
|
||||
var flag uint32
|
||||
var createdTime uint64
|
||||
var updatedTime uint64
|
||||
var dataType uint16
|
||||
var period uint16
|
||||
|
||||
err := cassandraClusterSession.Query(`SELECT owner_pid, size, name, meta_binary, flag, creation_date, update_date, data_type, period FROM pretendo_smm.courses WHERE data_id=?`, dataID).Scan(&ownerPID, &size, &name, &metaBinary, &flag, &createdTime, &updatedTime, &dataType, &period)
|
||||
|
||||
if err != nil {
|
||||
if err == gocql.ErrNotFound {
|
||||
return nil
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
var stars uint32
|
||||
var attempts uint32
|
||||
var failures uint32
|
||||
var completions uint32
|
||||
|
||||
_ = cassandraClusterSession.Query(`SELECT stars, attempts, failures, completions FROM pretendo_smm.ratings WHERE data_id=?`, dataID).Scan(&stars, &attempts, &failures, &completions)
|
||||
|
||||
courseMetadata := &types.CourseMetadata{
|
||||
DataID: dataID,
|
||||
OwnerPID: ownerPID,
|
||||
Size: size,
|
||||
CreatedTime: nex.NewDateTime(createdTime),
|
||||
UpdatedTime: nex.NewDateTime(updatedTime),
|
||||
Name: name,
|
||||
MetaBinary: metaBinary,
|
||||
Stars: stars,
|
||||
Attempts: attempts,
|
||||
Failures: failures,
|
||||
Completions: completions,
|
||||
Flag: flag,
|
||||
DataType: dataType,
|
||||
Period: period,
|
||||
}
|
||||
|
||||
return courseMetadata
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package database
|
||||
|
||||
import "github.com/PretendoNetwork/super-mario-maker-secure/types"
|
||||
|
||||
func GetCourseMetadataByDataIDs(dataIDs []uint64) []*types.CourseMetadata {
|
||||
// TODO: Do this in one query?
|
||||
courseMetadatas := make([]*types.CourseMetadata, 0)
|
||||
|
||||
for _, dataID := range dataIDs {
|
||||
courseMetadata := GetCourseMetadataByDataID(dataID)
|
||||
|
||||
if courseMetadata != nil {
|
||||
courseMetadatas = append(courseMetadatas, courseMetadata)
|
||||
}
|
||||
}
|
||||
|
||||
return courseMetadatas
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/types"
|
||||
)
|
||||
|
||||
func GetCourseMetadatasByLimit(limit uint32) []*types.CourseMetadata {
|
||||
var sliceMap []map[string]interface{}
|
||||
var err error
|
||||
|
||||
if sliceMap, err = cassandraClusterSession.Query(`SELECT data_id, owner_pid, size, name, meta_binary, flag, creation_date, update_date, data_type, period FROM pretendo_smm.courses LIMIT ?`, limit).Iter().SliceMap(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
courseMetadatas := make([]*types.CourseMetadata, 0)
|
||||
|
||||
for _, course := range sliceMap {
|
||||
dataID := uint64(course["data_id"].(int64))
|
||||
|
||||
var stars uint32
|
||||
var attempts uint32
|
||||
var failures uint32
|
||||
var completions uint32
|
||||
|
||||
_ = cassandraClusterSession.Query(`SELECT stars, attempts, failures, completions FROM pretendo_smm.ratings WHERE data_id=?`, dataID).Scan(&stars, &attempts, &failures, &completions)
|
||||
|
||||
courseMetadata := &types.CourseMetadata{
|
||||
DataID: dataID,
|
||||
OwnerPID: uint32(course["owner_pid"].(int)),
|
||||
Size: uint32(course["size"].(int)),
|
||||
CreatedTime: nex.NewDateTime(uint64(course["creation_date"].(int64))),
|
||||
UpdatedTime: nex.NewDateTime(uint64(course["update_date"].(int64))),
|
||||
Name: course["name"].(string),
|
||||
MetaBinary: course["meta_binary"].([]byte),
|
||||
Stars: stars,
|
||||
Attempts: attempts,
|
||||
Failures: failures,
|
||||
Completions: completions,
|
||||
Flag: uint32(course["flag"].(int)),
|
||||
DataType: uint16(course["data_type"].(int16)),
|
||||
Period: uint16(course["period"].(int16)),
|
||||
}
|
||||
|
||||
courseMetadatas = append(courseMetadatas, courseMetadata)
|
||||
}
|
||||
|
||||
return courseMetadatas
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/types"
|
||||
)
|
||||
|
||||
func GetCourseMetadatasByPID(pid uint32) []*types.CourseMetadata {
|
||||
courseMetadatas := make([]*types.CourseMetadata, 0)
|
||||
|
||||
// TODO: Fix this query? Seems like a weird way of doing this...
|
||||
var sliceMap []map[string]interface{}
|
||||
var err error
|
||||
|
||||
if sliceMap, err = cassandraClusterSession.Query(`SELECT data_id FROM pretendo_smm.courses WHERE owner_pid=? ALLOW FILTERING`, pid).Iter().SliceMap(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, course := range sliceMap {
|
||||
dataID := uint64(course["data_id"].(int64))
|
||||
courseMetadatas = append(courseMetadatas, GetCourseMetadataByDataID(dataID))
|
||||
}
|
||||
|
||||
return courseMetadatas
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/types"
|
||||
)
|
||||
|
||||
func GetCourseWorldRecord(dataID uint64) *types.CourseWorldRecord {
|
||||
var worldRecordFirstPID uint32
|
||||
var worldRecordPID uint32
|
||||
var worldRecordCreatedTime uint64
|
||||
var worldRecordUpdatedTime uint64
|
||||
var worldRecord int32
|
||||
|
||||
_ = cassandraClusterSession.Query(`SELECT world_record_first_pid, world_record_pid, world_record_creation_date, world_record_update_date, world_record FROM pretendo_smm.courses WHERE data_id=?`, dataID).Scan(&worldRecordFirstPID, &worldRecordPID, &worldRecordCreatedTime, &worldRecordUpdatedTime, &worldRecord)
|
||||
|
||||
if worldRecordFirstPID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.CourseWorldRecord{
|
||||
FirstPID: worldRecordFirstPID,
|
||||
BestPID: worldRecordPID,
|
||||
CreatedTime: nex.NewDateTime(worldRecordCreatedTime),
|
||||
UpdatedTime: nex.NewDateTime(worldRecordUpdatedTime),
|
||||
Score: worldRecord,
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package database
|
||||
|
||||
func GetDataStoreIDGeneratorLastID(nodeID int) uint32 {
|
||||
var lastID uint32
|
||||
_ = cassandraClusterSession.Query(`SELECT last_id FROM pretendo_smm.generator_last_id WHERE node_id=?`, nodeID).Scan(&lastID)
|
||||
|
||||
return lastID
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func GetUserMiiInfoByPID(pid uint32) bson.M {
|
||||
var result bson.M
|
||||
|
||||
err := mongoCollection.FindOne(context.TODO(), bson.D{{Key: "pid", Value: pid}}, options.FindOne()).Decode(&result)
|
||||
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil
|
||||
}
|
||||
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return result["mii"].(bson.M)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package database
|
||||
|
||||
import "github.com/PretendoNetwork/super-mario-maker-secure/types"
|
||||
|
||||
func GetUserStarredCourses(pid uint32) []*types.CourseMetadata {
|
||||
var dataIDs []uint64
|
||||
_ = cassandraClusterSession.Query(`SELECT starred_courses FROM pretendo_smm.user_play_info WHERE pid=?`, pid).Scan(&dataIDs)
|
||||
|
||||
return GetCourseMetadataByDataIDs(dataIDs)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func IncrementCourseAttemptCount(courseID uint64) {
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.ratings SET attempts=attempts+1 WHERE data_id=?`, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func IncrementCourseClearCount(courseID uint64) {
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.ratings SET completions=completions+1 WHERE data_id=?`, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func IncrementCourseFailCount(courseID uint64) {
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.ratings SET failures=failures+1 WHERE data_id=?`, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func IncrementCourseStarCount(courseID uint64) {
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.ratings SET stars=stars+1 WHERE data_id=?`, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
281
database/init_postgres.go
Normal file
281
database/init_postgres.go
Normal file
@@ -0,0 +1,281 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func initPostgres() {
|
||||
_, err := Postgres.Exec(`CREATE SCHEMA IF NOT EXISTS datastore`)
|
||||
if err != nil {
|
||||
globals.Logger.Critical(err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
globals.Logger.Success("datastore Postgres schema created")
|
||||
|
||||
// * Super Mario Maker has non-standard DataID requirements.
|
||||
// * DataID 900000 is reserved for the event course metadata
|
||||
// * file, and official event courses begin at DataID 930010
|
||||
// * and end at DataID 930050. To prevent a collision
|
||||
// * eventually, we need to start course IDs AFTER 930050
|
||||
// *
|
||||
// * DataIDs are stored and processed as uint64, however
|
||||
// * Super Mario Maker can not use the full uint64 range.
|
||||
// * This is because course share codes are generated from the
|
||||
// * courses DataID. A course share code is an 8 byte hex
|
||||
// * string, where the upper 2 bytes are the checksum of the
|
||||
// * lower 6 bytes. The lower 6 bytes are the courses DataID
|
||||
// *
|
||||
// * Super Mario Maker is only capable of displaying codes up
|
||||
// * to 0xFFFFFFFFFFFF, essentially truncating DataIDs down to
|
||||
// * 48 bit integers instead of 64 bit. I doubt we will ever
|
||||
// * hit even the 32 bit limit, let alone 48, but this is here
|
||||
// * just in case
|
||||
_, err = Postgres.Exec(`CREATE SEQUENCE IF NOT EXISTS datastore.object_data_id_seq
|
||||
INCREMENT 1
|
||||
MINVALUE 1
|
||||
MAXVALUE 281474976710656
|
||||
START 940000
|
||||
CACHE 1`, // * Honestly I don't know what CACHE does but I saw it recommended so here it is
|
||||
)
|
||||
if err != nil {
|
||||
globals.Logger.Critical(err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// * "deletion_reason" and "under_review" are specific to SMM.
|
||||
// * Everything else is stock
|
||||
_, err = Postgres.Exec(`CREATE TABLE IF NOT EXISTS datastore.objects (
|
||||
data_id bigint NOT NULL DEFAULT nextval('datastore.object_data_id_seq') PRIMARY KEY,
|
||||
upload_completed boolean NOT NULL DEFAULT FALSE,
|
||||
deleted boolean NOT NULL DEFAULT FALSE,
|
||||
deletion_reason int NOT NULL DEFAULT 0,
|
||||
under_review boolean NOT NULL DEFAULT FALSE,
|
||||
owner int,
|
||||
size int,
|
||||
name text,
|
||||
data_type int,
|
||||
meta_binary bytea,
|
||||
permission int,
|
||||
permission_recipients int[],
|
||||
delete_permission int,
|
||||
delete_permission_recipients int[],
|
||||
flag int,
|
||||
period int,
|
||||
refer_data_id bigint,
|
||||
tags text[],
|
||||
persistence_slot_id int,
|
||||
extra_data text[],
|
||||
access_password bigint NOT NULL DEFAULT 0,
|
||||
update_password bigint NOT NULL DEFAULT 0,
|
||||
creation_date timestamp,
|
||||
update_date timestamp
|
||||
)`)
|
||||
if err != nil {
|
||||
globals.Logger.Critical(err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// * Unsure what like half of this is but the client sends it so we saves it
|
||||
_, err = Postgres.Exec(`CREATE TABLE IF NOT EXISTS datastore.object_ratings (
|
||||
data_id bigint,
|
||||
slot smallint,
|
||||
flag smallint,
|
||||
internal_flag smallint,
|
||||
lock_type smallint,
|
||||
initial_value bigint,
|
||||
range_min int,
|
||||
range_max int,
|
||||
period_hour smallint,
|
||||
period_duration int,
|
||||
total_value bigint,
|
||||
count int NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(data_id, slot)
|
||||
)`)
|
||||
if err != nil {
|
||||
globals.Logger.Critical(err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// * Custom rankings are specific to SMM
|
||||
// TODO - Store the period? What even is the period of custom rankings?
|
||||
_, err = Postgres.Exec(`CREATE TABLE IF NOT EXISTS datastore.object_custom_rankings (
|
||||
data_id bigint,
|
||||
application_id bigint,
|
||||
value bigint,
|
||||
PRIMARY KEY(data_id, application_id)
|
||||
)`)
|
||||
if err != nil {
|
||||
globals.Logger.Critical(err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// * BufferQueues are specific to SMM
|
||||
// * Real server does not allow duplicate buffers in a given slot for an object,
|
||||
// * even if uploaded by different users. We could change this, but I don't see
|
||||
// * much point
|
||||
_, err = Postgres.Exec(`CREATE TABLE IF NOT EXISTS datastore.buffer_queues (
|
||||
data_id bigint,
|
||||
slot int,
|
||||
creation_date timestamp,
|
||||
buffer bytea,
|
||||
PRIMARY KEY(data_id, slot, buffer)
|
||||
)`)
|
||||
if err != nil {
|
||||
globals.Logger.Critical(err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// * Course records are specific to SMM
|
||||
_, err = Postgres.Exec(`CREATE TABLE IF NOT EXISTS datastore.course_records (
|
||||
data_id bigint,
|
||||
slot int,
|
||||
first_pid int,
|
||||
best_pid int,
|
||||
best_score int,
|
||||
creation_date timestamp,
|
||||
update_date timestamp,
|
||||
PRIMARY KEY(data_id, slot)
|
||||
)`)
|
||||
if err != nil {
|
||||
globals.Logger.Critical(err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
globals.Logger.Success("Postgres tables created")
|
||||
|
||||
ensureEventCourseMetaDataFileExists()
|
||||
}
|
||||
|
||||
func ensureEventCourseMetaDataFileExists() {
|
||||
// * The event course metadata file is REQUIRED for
|
||||
// * Super Mario Maker to open Course World. This
|
||||
// * ensures that it exists both in S3 and in the
|
||||
// * database before booting. It has the reserved
|
||||
// * DataID 900000
|
||||
bucket := os.Getenv("PN_SMM_CONFIG_S3_BUCKET")
|
||||
key := "900000.bin"
|
||||
|
||||
objectSizeS3, err := globals.S3ObjectSize(bucket, key)
|
||||
if err != nil {
|
||||
globals.Logger.Errorf("Failed to stat event course metadata file. Ensure your S3 credentials are correct and the 900000.bin file is uploaded to your bucket. S3 error: %s", err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
globals.Logger.Success("Event course metadata file found. Verifying database")
|
||||
|
||||
var exists bool
|
||||
err = Postgres.QueryRow(`SELECT EXISTS(SELECT 1 FROM datastore.objects WHERE data_id=900000) AS "exists"`).Scan(&exists)
|
||||
if err != nil {
|
||||
globals.Logger.Errorf("Error querying for event course metadata object in Postgres: %s", err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
if !exists {
|
||||
var dataID uint64
|
||||
|
||||
globals.Logger.Info("Event course metadata object not found in Postgres. Creating")
|
||||
|
||||
err := Postgres.QueryRow(`INSERT INTO datastore.objects (
|
||||
data_id,
|
||||
upload_completed,
|
||||
owner,
|
||||
size,
|
||||
name,
|
||||
data_type,
|
||||
meta_binary,
|
||||
permission,
|
||||
permission_recipients,
|
||||
delete_permission,
|
||||
delete_permission_recipients,
|
||||
flag,
|
||||
period,
|
||||
refer_data_id,
|
||||
tags,
|
||||
persistence_slot_id,
|
||||
extra_data,
|
||||
creation_date,
|
||||
update_date
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9,
|
||||
$10,
|
||||
$11,
|
||||
$12,
|
||||
$13,
|
||||
$14,
|
||||
$15,
|
||||
$16,
|
||||
$17,
|
||||
$18,
|
||||
$19
|
||||
) RETURNING data_id`,
|
||||
900000,
|
||||
true,
|
||||
2, // * "Quazal Rendez-Vous" special account
|
||||
objectSizeS3,
|
||||
"", // * Has no name
|
||||
50, // * Metadata file has DataType 50. Event courses have DataType 51
|
||||
[]byte{}, // * No MetaBinary
|
||||
0, // * Accessible by everyone
|
||||
pq.Array([]uint32{}),
|
||||
3, // * THE REAL SERVER HAS THIS SET TO 0, FOR EVERYONE. THAT'S SUPER INSECURE.
|
||||
pq.Array([]uint32{}),
|
||||
0,
|
||||
64306,
|
||||
0,
|
||||
pq.Array([]string{}),
|
||||
0, // * Unsure what the slot ID actually is
|
||||
pq.Array([]string{}),
|
||||
now,
|
||||
now,
|
||||
).Scan(&dataID)
|
||||
if err != nil {
|
||||
globals.Logger.Errorf("Error creating event course metadata object: %s", err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
} else {
|
||||
var objectSizeDB uint32
|
||||
|
||||
err := Postgres.QueryRow(`SELECT size FROM datastore.objects WHERE data_id=900000`).Scan(&objectSizeDB)
|
||||
if err != nil {
|
||||
globals.Logger.Errorf("Error querying event course metadata object size: %s", err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if objectSizeS3 != uint64(objectSizeDB) {
|
||||
globals.Logger.Success("Event course metadata object found in Postgres! Updating size")
|
||||
|
||||
_, err := Postgres.Exec(`UPDATE datastore.objects SET size=$1, update_date=$2 WHERE data_id=900000`, objectSizeS3, now)
|
||||
if err != nil {
|
||||
globals.Logger.Errorf("Error updating event course metadata object size: %s", err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
} else {
|
||||
globals.Logger.Success("Event course metadata object found in Postgres!")
|
||||
}
|
||||
|
||||
globals.Logger.Info("Ensuring event course metadata object has correct delete permission")
|
||||
|
||||
_, err = Postgres.Exec(`UPDATE datastore.objects SET delete_permission=3 WHERE data_id=900000`)
|
||||
if err != nil {
|
||||
globals.Logger.Errorf("Error updating event course metadata object delete permission: %s", err.Error())
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
globals.Logger.Success("Event course metadata object found and is up to date!")
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
)
|
||||
|
||||
func InitializeCourseData(courseID uint64, ownerPID uint32, size uint32, name string, flag uint32, extraData []string, dataType uint16, period uint16) {
|
||||
datetime := nex.NewDateTime(0)
|
||||
now := datetime.Now()
|
||||
|
||||
if err := cassandraClusterSession.Query(`INSERT INTO pretendo_smm.courses(
|
||||
data_id,
|
||||
owner_pid,
|
||||
size,
|
||||
name,
|
||||
flag,
|
||||
extra_data,
|
||||
playable,
|
||||
creation_date,
|
||||
update_date,
|
||||
world_record_first_pid,
|
||||
world_record_pid,
|
||||
world_record_creation_date,
|
||||
world_record_update_date,
|
||||
world_record,
|
||||
data_type,
|
||||
period
|
||||
)
|
||||
VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
false,
|
||||
?,
|
||||
?,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
?,
|
||||
?
|
||||
) IF NOT EXISTS`,
|
||||
courseID, ownerPID, size, name, flag, extraData, now, now, dataType, period).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.ratings SET stars=stars+0, attempts=attempts+0, failures=failures+0, completions=completions+0 WHERE data_id=?`, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func InsertBufferQueueData(dataID uint64, slot uint32, buffer []byte) {
|
||||
if err := cassandraClusterSession.Query(`INSERT INTO pretendo_smm.buffer_queues( id, data_id, slot, buffer ) VALUES ( now(), ?, ?, ? ) IF NOT EXISTS`, dataID, slot, buffer).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func SetCoursePlayable(courseID uint64) {
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.courses SET playable=true WHERE data_id=?`, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func SetDataStoreIDGeneratorLastID(nodeID int, value uint32) {
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.generator_last_id SET last_id=? WHERE node_id=?`, value, nodeID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package database
|
||||
|
||||
import "log"
|
||||
|
||||
func UpdateCourseMetaBinary(courseID uint64, metaBinary []byte) {
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.courses SET meta_binary=? WHERE data_id=?`, metaBinary, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
)
|
||||
|
||||
func UpdateCourseWorldRecord(courseID uint64, ownerPID uint32, score int32) {
|
||||
datetime := nex.NewDateTime(0)
|
||||
now := datetime.Now()
|
||||
|
||||
if GetCourseWorldRecord(courseID) == nil {
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.courses SET world_record_first_pid=?, world_record_creation_date=? WHERE data_id=?`, ownerPID, now, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := cassandraClusterSession.Query(`UPDATE pretendo_smm.courses SET world_record_pid=?, world_record_update_date=?, world_record=? WHERE data_id=?`, ownerPID, now, score, courseID).Exec(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,19 @@ package globals
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/plogger-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
pb "github.com/PretendoNetwork/grpc-go/account"
|
||||
)
|
||||
|
||||
var Logger = plogger.NewLogger()
|
||||
var NEXServer *nex.Server
|
||||
var S3Client *s3.Client
|
||||
var HMACSecret []byte
|
||||
var DataStoreIDGenerators []*database.DataStoreIDGenerator
|
||||
var KerberosPassword = "password" // * Default password
|
||||
var AuthenticationServer *nex.Server
|
||||
var SecureServer *nex.Server
|
||||
var MinIOClient *minio.Client
|
||||
var Presigner *S3Presigner
|
||||
var GRPCAccountClientConnection *grpc.ClientConn
|
||||
var GRPCAccountClient pb.AccountClient
|
||||
var GRPCAccountCommonMetadata metadata.MD
|
||||
|
||||
22
globals/password_from_pid.go
Executable file
22
globals/password_from_pid.go
Executable file
@@ -0,0 +1,22 @@
|
||||
package globals
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "github.com/PretendoNetwork/grpc-go/account"
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/globals"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func PasswordFromPID(pid uint32) (string, uint32) {
|
||||
ctx := metadata.NewOutgoingContext(context.Background(), GRPCAccountCommonMetadata)
|
||||
|
||||
response, err := GRPCAccountClient.GetNEXPassword(ctx, &pb.GetNEXPasswordRequest{Pid: pid})
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return "", nex.Errors.RendezVous.InvalidUsername
|
||||
}
|
||||
|
||||
return response.Password, 0
|
||||
}
|
||||
20
globals/s3_helpers.go
Normal file
20
globals/s3_helpers.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package globals
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
)
|
||||
|
||||
func S3StatObject(bucket, key string) (minio.ObjectInfo, error) {
|
||||
return MinIOClient.StatObject(context.TODO(), bucket, key, minio.StatObjectOptions{})
|
||||
}
|
||||
|
||||
func S3ObjectSize(bucket, key string) (uint64, error) {
|
||||
info, err := S3StatObject(bucket, key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint64(info.Size), nil
|
||||
}
|
||||
46
globals/s3_presigner.go
Normal file
46
globals/s3_presigner.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package globals
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
40
globals/verify_object_permission.go
Normal file
40
globals/verify_object_permission.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package globals
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func VerifyObjectPermission(ownerPID, accessorPID uint32, permission *datastore_types.DataStorePermission) uint32 {
|
||||
if permission.Permission > 3 {
|
||||
return nex.Errors.DataStore.InvalidArgument
|
||||
}
|
||||
|
||||
// * Allow anyone
|
||||
if permission.Permission == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// * Allow friends
|
||||
// TODO - Implement this
|
||||
if permission.Permission == 1 {
|
||||
return nex.Errors.DataStore.PermissionDenied
|
||||
}
|
||||
|
||||
// * Allow people in permission.RecipientIDs
|
||||
if permission.Permission == 2 {
|
||||
if !slices.Contains(permission.RecipientIDs, accessorPID) {
|
||||
return nex.Errors.DataStore.PermissionDenied
|
||||
}
|
||||
}
|
||||
|
||||
// * Allow only the owner
|
||||
if permission.Permission == 3 {
|
||||
if ownerPID != accessorPID {
|
||||
return nex.Errors.DataStore.PermissionDenied
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
69
go.mod
69
go.mod
@@ -1,48 +1,45 @@
|
||||
module github.com/PretendoNetwork/super-mario-maker-secure
|
||||
|
||||
go 1.19
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/PretendoNetwork/nex-go v1.0.16
|
||||
github.com/PretendoNetwork/nex-protocols-common-go v1.0.17
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.20
|
||||
github.com/PretendoNetwork/plogger-go v1.0.2
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.2
|
||||
github.com/gocql/gocql v1.3.2
|
||||
github.com/PretendoNetwork/grpc-go v1.0.2
|
||||
github.com/PretendoNetwork/nex-go v1.0.41
|
||||
github.com/PretendoNetwork/nex-protocols-common-go v1.0.30
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.58
|
||||
github.com/PretendoNetwork/plogger-go v1.0.4
|
||||
github.com/joho/godotenv v1.5.1
|
||||
go.mongodb.org/mongo-driver v1.11.4
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/minio/minio-go/v7 v7.0.63
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
||||
google.golang.org/grpc v1.59.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.1 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
|
||||
github.com/jwalton/go-supportscolor v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/uuid v1.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/jwalton/go-supportscolor v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.17.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.1 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/term v0.7.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/term v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
||||
179
go.sum
179
go.sum
@@ -1,125 +1,102 @@
|
||||
github.com/PretendoNetwork/nex-go v1.0.16 h1:g2lJW8G+WOS6/8qsgQ0+jLlzemjJ60tQtLqloLTjElo=
|
||||
github.com/PretendoNetwork/nex-go v1.0.16/go.mod h1:Bx2ONeSefnbJyE0IDIwGopxrjRrnszOV/uQv74Cx+m0=
|
||||
github.com/PretendoNetwork/nex-protocols-common-go v1.0.17 h1:w0n+RJAHANgX/iVnL5qRiyPJeDHcwr8w6DzDeFqxkqk=
|
||||
github.com/PretendoNetwork/nex-protocols-common-go v1.0.17/go.mod h1:5Y3jRIbvDxasj0CIvd024za+t9LMbD0OZkBwyKICijg=
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.20 h1:vUoYHFvVWWx9Bm9/2JiN1siW03tFxtD4NY9w/WfC878=
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.20/go.mod h1:Bt7hI7hJ+8r6UU5LSs+3xx4xzsvMVeEUkAW8Cg3lGjQ=
|
||||
github.com/PretendoNetwork/plogger-go v1.0.2 h1:vWKEnEmJJzYwqLxLyiSsAvCrZV6qnnu/a0GQOjIfzY0=
|
||||
github.com/PretendoNetwork/plogger-go v1.0.2/go.mod h1:7kD6M4vPq1JL4LTuPg6kuB1OvUBOwQOtAvTaUwMbwvU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.8 h1:GMupCNNI7FARX27L7GjCJM8NgivWbRgpjNI/hOQjFS8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32 h1:dpbVNUjczQ8Ae3QKHbpHBpfvaVkRdesxpTOe9pTouhU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26 h1:QH2kOS3Ht7x+u0gHCh06CXL/h6G8LQJFpZfFBYBNboo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26/go.mod h1:vq86l7956VgFr0/FWQ2BWnK07QC3WYsepKzy33qqY5U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.24 h1:zsg+5ouVLLbePknVZlUMm1ptwyQLkjjLMWnN+kVs5dA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.24/go.mod h1:+fFaIjycTmpV6hjmPTbyU9Kp5MI/lA+bbibcAtmlhYA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.27 h1:qIw7Hg5eJEc1uSxg3hRwAthPAO7NeOd4dPxhaTi0yB0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.27/go.mod h1:Zz0kvhcSlu3NX4XJkaGgdjaa+u7a9LYuy8JKxA5v3RM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26 h1:uUt4XctZLhl9wBE1L8lobU3bVN8SNUP7T+olb0bWBO4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26/go.mod h1:Bd4C/4PkVGubtNe5iMXu5BNnaBi/9t/UsFspPt4ram8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.1 h1:lRWp3bNu5wy0X3a8GS42JvZFlv++AKsMdzEnoiVJrkg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.1/go.mod h1:VXBHSxdN46bsJrkniN68psSwbyBKsazQfU2yX/iSDso=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.2 h1:iOZoYePk+EuBI1tC7bxeRjO+JvClcYm2fZYW5WPIOMQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.2/go.mod h1:aSl9/LJltSz1cVusiR/Mu8tvI4Sv/5w/WWrJmmkNii0=
|
||||
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/PretendoNetwork/grpc-go v1.0.2 h1:9TvKmX7dCOANyoHEra1MMYqS1N/RGav66TRG4SHInvo=
|
||||
github.com/PretendoNetwork/grpc-go v1.0.2/go.mod h1:XZjEsij9lL7HJBNkH6JPbBIkUSq/1rjflvjGdv+DAj0=
|
||||
github.com/PretendoNetwork/nex-go v1.0.41 h1:TcBb1Bpe2KAB+AXaGX1K9NVQBRtZJIoy4yCvRde2xbI=
|
||||
github.com/PretendoNetwork/nex-go v1.0.41/go.mod h1:QwHEa165DeVd0xIuthrgc3j6NWXT8lyPSG6t5kC/Shk=
|
||||
github.com/PretendoNetwork/nex-protocols-common-go v1.0.30 h1:z2BvyG/3x1lW8sx6Ml83G0Lkj5J9hdb0bjdZuuDCYSE=
|
||||
github.com/PretendoNetwork/nex-protocols-common-go v1.0.30/go.mod h1:37jNRX6M/s0xA/gc4BccO8k2GLG1lpMZXAtIYRCZCTY=
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.58 h1:W8pZ2LOlJm/mo/UOJjkLWNVHs0jQmkoa8qoI5A1WVyY=
|
||||
github.com/PretendoNetwork/nex-protocols-go v1.0.58/go.mod h1:136762CMIcAsTZDrt4W7gDE4ppiBuc9zN2QFOHESjS8=
|
||||
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/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.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/gocql/gocql v1.3.2 h1:ox3T+R7VFibHSIGxRkuUi1uIvAv8jBHCWxc+9aFQ/LA=
|
||||
github.com/gocql/gocql v1.3.2/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.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.1.0 h1:HsXFJdMPjRUAx8cIW6g30hVSFYaxh9yRQwEWgkAR7lQ=
|
||||
github.com/jwalton/go-supportscolor v1.1.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
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/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
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.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ=
|
||||
github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4=
|
||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
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/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7 h1:N9RLxaR65C36i26BUIpzPXGy2f6pQ7wisu2bawbKNqg=
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7/go.mod h1:4ub2EKgF1MAhTjoOCTU4b9uLMsAweHEa89aRrfAypXA=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
|
||||
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas=
|
||||
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
129
init.go
129
init.go
@@ -1,13 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
pb "github.com/PretendoNetwork/grpc-go/account"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -18,26 +26,107 @@ func init() {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
|
||||
globals.HMACSecret, err = ioutil.ReadFile("secret.key")
|
||||
s3Endpoint := os.Getenv("PN_SMM_CONFIG_S3_ENDPOINT")
|
||||
s3AccessKey := os.Getenv("PN_SMM_CONFIG_S3_ACCESS_KEY")
|
||||
s3AccessSecret := os.Getenv("PN_SMM_CONFIG_S3_ACCESS_SECRET")
|
||||
|
||||
postgresURI := os.Getenv("PN_SMM_POSTGRES_URI")
|
||||
kerberosPassword := os.Getenv("PN_SMM_KERBEROS_PASSWORD")
|
||||
authenticationServerPort := os.Getenv("PN_SMM_AUTHENTICATION_SERVER_PORT")
|
||||
secureServerHost := os.Getenv("PN_SMM_SECURE_SERVER_HOST")
|
||||
secureServerPort := os.Getenv("PN_SMM_SECURE_SERVER_PORT")
|
||||
accountGRPCHost := os.Getenv("PN_SMM_ACCOUNT_GRPC_HOST")
|
||||
accountGRPCPort := os.Getenv("PN_SMM_ACCOUNT_GRPC_PORT")
|
||||
accountGRPCAPIKey := os.Getenv("PN_SMM_ACCOUNT_GRPC_API_KEY")
|
||||
|
||||
if strings.TrimSpace(postgresURI) == "" {
|
||||
globals.Logger.Error("PN_SMM_POSTGRES_URI environment variable not set")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(kerberosPassword) == "" {
|
||||
globals.Logger.Warningf("PN_SMM_KERBEROS_PASSWORD environment variable not set. Using default password: %q", globals.KerberosPassword)
|
||||
} else {
|
||||
globals.KerberosPassword = kerberosPassword
|
||||
}
|
||||
|
||||
if strings.TrimSpace(authenticationServerPort) == "" {
|
||||
globals.Logger.Error("PN_SMM_AUTHENTICATION_SERVER_PORT environment variable not set")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if port, err := strconv.Atoi(authenticationServerPort); err != nil {
|
||||
globals.Logger.Errorf("PN_SMM_AUTHENTICATION_SERVER_PORT is not a valid port. Expected 0-65535, got %s", authenticationServerPort)
|
||||
os.Exit(0)
|
||||
} else if port < 0 || port > 65535 {
|
||||
globals.Logger.Errorf("PN_SMM_AUTHENTICATION_SERVER_PORT is not a valid port. Expected 0-65535, got %s", authenticationServerPort)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(secureServerHost) == "" {
|
||||
globals.Logger.Error("PN_SMM_SECURE_SERVER_HOST environment variable not set")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(secureServerPort) == "" {
|
||||
globals.Logger.Error("PN_SMM_SECURE_SERVER_PORT environment variable not set")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if port, err := strconv.Atoi(secureServerPort); err != nil {
|
||||
globals.Logger.Errorf("PN_SMM_SECURE_SERVER_PORT is not a valid port. Expected 0-65535, got %s", secureServerPort)
|
||||
os.Exit(0)
|
||||
} else if port < 0 || port > 65535 {
|
||||
globals.Logger.Errorf("PN_SMM_SECURE_SERVER_PORT is not a valid port. Expected 0-65535, got %s", secureServerPort)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(accountGRPCHost) == "" {
|
||||
globals.Logger.Error("PN_SMM_ACCOUNT_GRPC_HOST environment variable not set")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(accountGRPCPort) == "" {
|
||||
globals.Logger.Error("PN_SMM_ACCOUNT_GRPC_PORT environment variable not set")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if port, err := strconv.Atoi(accountGRPCPort); err != nil {
|
||||
globals.Logger.Errorf("PN_SMM_ACCOUNT_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort)
|
||||
os.Exit(0)
|
||||
} else if port < 0 || port > 65535 {
|
||||
globals.Logger.Errorf("PN_SMM_ACCOUNT_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(accountGRPCAPIKey) == "" {
|
||||
globals.Logger.Warning("Insecure gRPC server detected. PN_SMM_ACCOUNT_GRPC_API_KEY environment variable not set")
|
||||
}
|
||||
|
||||
globals.GRPCAccountClientConnection, err = grpc.Dial(fmt.Sprintf("%s:%s", accountGRPCHost, accountGRPCPort), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
globals.Logger.Criticalf("Failed to connect to account gRPC server: %v", err)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
globals.GRPCAccountClient = pb.NewAccountClient(globals.GRPCAccountClientConnection)
|
||||
globals.GRPCAccountCommonMetadata = metadata.Pairs(
|
||||
"X-API-Key", accountGRPCAPIKey,
|
||||
)
|
||||
|
||||
staticCredentials := credentials.NewStaticV4(s3AccessKey, s3AccessSecret, "")
|
||||
|
||||
minIOClient, err := minio.New(s3Endpoint, &minio.Options{
|
||||
Creds: staticCredentials,
|
||||
Secure: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Connect to and setup databases
|
||||
database.ConnectAll()
|
||||
createDataStoreIDGenerators()
|
||||
}
|
||||
|
||||
func createDataStoreIDGenerators() {
|
||||
globals.DataStoreIDGenerators = make([]*database.DataStoreIDGenerator, 0)
|
||||
regionID := 0 // USA
|
||||
|
||||
for corenum := 0; corenum < runtime.NumCPU(); corenum++ {
|
||||
database.CreateDataStoreIDGeneratorRow(corenum)
|
||||
|
||||
lastID := database.GetDataStoreIDGeneratorLastID(corenum)
|
||||
|
||||
generator := database.NewDataStoreIDGenerator(uint8(regionID), uint8(corenum), lastID)
|
||||
globals.DataStoreIDGenerators = append(globals.DataStoreIDGenerators, generator)
|
||||
}
|
||||
globals.MinIOClient = minIOClient
|
||||
globals.Presigner = globals.NewS3Presigner(globals.MinIOClient)
|
||||
|
||||
// * Connect to and setup databases
|
||||
database.ConnectPostgres()
|
||||
}
|
||||
|
||||
5
main.go
5
main.go
@@ -9,10 +9,11 @@ import (
|
||||
var wg sync.WaitGroup
|
||||
|
||||
func main() {
|
||||
wg.Add(1)
|
||||
wg.Add(2)
|
||||
|
||||
// TODO - Add gRPC server
|
||||
go nex.StartNEXServer()
|
||||
go nex.StartAuthenticationServer()
|
||||
go nex.StartSecureServer()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
35
nex/authentication.go
Normal file
35
nex/authentication.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package nex
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
var serverBuildString string
|
||||
|
||||
func StartAuthenticationServer() {
|
||||
globals.AuthenticationServer = nex.NewServer()
|
||||
globals.AuthenticationServer.SetPRUDPVersion(1)
|
||||
globals.AuthenticationServer.SetPRUDPProtocolMinorVersion(2)
|
||||
globals.AuthenticationServer.SetDefaultNEXVersion(nex.NewPatchedNEXVersion(3, 8, 3, "AMAJ"))
|
||||
globals.AuthenticationServer.SetKerberosPassword(globals.KerberosPassword)
|
||||
globals.AuthenticationServer.SetAccessKey("9f2b4678")
|
||||
|
||||
globals.AuthenticationServer.On("Data", func(packet *nex.PacketV1) {
|
||||
request := packet.RMCRequest()
|
||||
|
||||
fmt.Println("==SMM1 - Auth==")
|
||||
fmt.Printf("Protocol ID: %d\n", request.ProtocolID())
|
||||
fmt.Printf("Method ID: %d\n", request.MethodID())
|
||||
fmt.Println("===============")
|
||||
})
|
||||
|
||||
serverBuildString = "build:3_8_29_3022_0"
|
||||
|
||||
registerCommonAuthenticationServerProtocols()
|
||||
|
||||
globals.AuthenticationServer.Listen(fmt.Sprintf(":%s", os.Getenv("PN_SMM_AUTHENTICATION_SERVER_PORT")))
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package nex_datastore
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func ChangeMeta(err error, client *nex.Client, callID uint32, dataStoreChangeMetaParam *datastore.DataStoreChangeMetaParam) {
|
||||
// STUBBED
|
||||
// TODO: DO SOMETHING WITH THE DATA
|
||||
rmcResponse := nex.NewRMCResponse(datastore.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore.MethodChangeMeta, nil)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
responsePacket, _ := nex.NewPacketV1(client, nil)
|
||||
|
||||
responsePacket.SetVersion(1)
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package nex_datastore
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func CompletePostObject(err error, client *nex.Client, callID uint32, dataStoreCompletePostParam *datastore.DataStoreCompletePostParam) {
|
||||
// TODO: DO SOMETHING WITH THE DATA
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore.MethodCompletePostObject, nil)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
responsePacket, _ := nex.NewPacketV1(client, nil)
|
||||
|
||||
responsePacket.SetVersion(1)
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package nex_datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/utility"
|
||||
)
|
||||
|
||||
func GetMeta(err error, client *nex.Client, callID uint32, param *datastore.DataStoreGetMetaParam) {
|
||||
switch param.DataID {
|
||||
case 0: // Mii Data
|
||||
getMetaMiiData(client, callID, param)
|
||||
case 900000: // Event course news
|
||||
getMetaEventCourseNewsData(client, callID, param)
|
||||
default:
|
||||
fmt.Printf("[Warning] DataStoreProtocol::GetMeta Unsupported dataId: %v\n", param.DataID)
|
||||
}
|
||||
}
|
||||
|
||||
func getMetaMiiData(client *nex.Client, callID uint32, param *datastore.DataStoreGetMetaParam) {
|
||||
miiInfo := database.GetUserMiiInfoByPID(param.PersistenceTarget.OwnerID)
|
||||
|
||||
pMetaInfo := utility.UserMiiDataToDataStoreMetaInfo(param.PersistenceTarget.OwnerID, miiInfo)
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
rmcResponseStream.WriteStructure(pMetaInfo)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore.MethodGetMeta, rmcResponseBody)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
responsePacket, _ := nex.NewPacketV1(client, nil)
|
||||
|
||||
responsePacket.SetVersion(1)
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
|
||||
func getMetaEventCourseNewsData(client *nex.Client, callID uint32, param *datastore.DataStoreGetMetaParam) {
|
||||
objectSize, _ := utility.S3ObjectSize(os.Getenv("S3_BUCKET_NAME"), "special/900000.bin")
|
||||
|
||||
pMetaInfo := datastore.NewDataStoreMetaInfo()
|
||||
pMetaInfo.DataID = 900000
|
||||
pMetaInfo.OwnerID = 2
|
||||
pMetaInfo.Size = uint32(objectSize)
|
||||
pMetaInfo.Name = ""
|
||||
pMetaInfo.DataType = 50 // Metdata?
|
||||
pMetaInfo.MetaBinary = []byte{}
|
||||
pMetaInfo.Permission = datastore.NewDataStorePermission()
|
||||
pMetaInfo.Permission.Permission = 0 // idk?
|
||||
pMetaInfo.Permission.RecipientIds = []uint32{}
|
||||
pMetaInfo.DelPermission = datastore.NewDataStorePermission()
|
||||
pMetaInfo.DelPermission.Permission = 0 // idk?
|
||||
pMetaInfo.DelPermission.RecipientIds = []uint32{}
|
||||
pMetaInfo.CreatedTime = nex.NewDateTime(135271087238) // Reused from Nintendo
|
||||
pMetaInfo.UpdatedTime = nex.NewDateTime(135402751254) // Reused from Nintendo
|
||||
pMetaInfo.Period = 64306 // idk?
|
||||
pMetaInfo.Status = 0
|
||||
pMetaInfo.ReferredCnt = 0
|
||||
pMetaInfo.ReferDataID = 0
|
||||
pMetaInfo.Flag = 0 // idk?
|
||||
pMetaInfo.ReferredTime = nex.NewDateTime(135271087238) // Reused from Nintendo
|
||||
pMetaInfo.ExpireTime = nex.NewDateTime(671075926016) // Reused from Nintendo
|
||||
pMetaInfo.Tags = []string{} // idk?
|
||||
pMetaInfo.Ratings = []*datastore.DataStoreRatingInfoWithSlot{}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
rmcResponseStream.WriteStructure(pMetaInfo)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore.MethodGetMeta, rmcResponseBody)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
responsePacket, _ := nex.NewPacketV1(client, nil)
|
||||
|
||||
responsePacket.SetVersion(1)
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package nex_datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/utility"
|
||||
)
|
||||
|
||||
func GetMetasMultipleParam(err error, client *nex.Client, callID uint32, params []*datastore.DataStoreGetMetaParam) {
|
||||
pMetaInfo := make([]*datastore.DataStoreMetaInfo, 0)
|
||||
pResults := make([]uint32, 0)
|
||||
|
||||
for _, param := range params {
|
||||
if param.DataID == 0 {
|
||||
pMetaInfo = append(pMetaInfo, getMetasMultipleParamMiiData(param))
|
||||
} else {
|
||||
fmt.Println("Unknown meta multiple data ID")
|
||||
}
|
||||
|
||||
pResults = append(pResults, 0x690001)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
|
||||
rmcResponseStream.WriteListStructure(pMetaInfo)
|
||||
rmcResponseStream.WriteListUInt32LE(pResults)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore.MethodGetMetasMultipleParam, rmcResponseBody)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
responsePacket, _ := nex.NewPacketV1(client, nil)
|
||||
|
||||
responsePacket.SetVersion(1)
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
|
||||
func getMetasMultipleParamMiiData(param *datastore.DataStoreGetMetaParam) *datastore.DataStoreMetaInfo {
|
||||
miiInfo := database.GetUserMiiInfoByPID(param.PersistenceTarget.OwnerID)
|
||||
|
||||
return utility.UserMiiDataToDataStoreMetaInfo(param.PersistenceTarget.OwnerID, miiInfo)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package nex_datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/utility"
|
||||
)
|
||||
|
||||
func PrepareGetObject(err error, client *nex.Client, callID uint32, dataStorePrepareGetParam *datastore.DataStorePrepareGetParam) {
|
||||
pReqGetInfo := datastore.NewDataStoreReqGetInfo()
|
||||
|
||||
if dataStorePrepareGetParam.DataID == 900000 {
|
||||
objectSize, _ := utility.S3ObjectSize(os.Getenv("S3_BUCKET_NAME"), "special/900000.bin")
|
||||
|
||||
pReqGetInfo.URL = fmt.Sprintf("http://%s.b-cdn.net/special/900000.bin", "pds-AMAJ-d1")
|
||||
pReqGetInfo.RequestHeaders = []*datastore.DataStoreKeyValue{}
|
||||
pReqGetInfo.Size = uint32(objectSize)
|
||||
pReqGetInfo.RootCA = []byte{}
|
||||
pReqGetInfo.DataID = 900000
|
||||
} else {
|
||||
courseMetadata := database.GetCourseMetadataByDataID(dataStorePrepareGetParam.DataID)
|
||||
|
||||
pReqGetInfo.URL = fmt.Sprintf("http://%s.b-cdn.net/course/%d.bin", os.Getenv("S3_BUCKET_NAME"), dataStorePrepareGetParam.DataID)
|
||||
pReqGetInfo.RequestHeaders = []*datastore.DataStoreKeyValue{}
|
||||
pReqGetInfo.Size = courseMetadata.Size
|
||||
pReqGetInfo.RootCA = []byte{}
|
||||
pReqGetInfo.DataID = dataStorePrepareGetParam.DataID
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
|
||||
rmcResponseStream.WriteStructure(pReqGetInfo)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore.MethodPrepareGetObject, rmcResponseBody)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
responsePacket, _ := nex.NewPacketV1(client, nil)
|
||||
|
||||
responsePacket.SetVersion(1)
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package nex_datastore
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func PreparePostObject(err error, client *nex.Client, callID uint32, param *datastore.DataStorePreparePostParam) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
nodeID := rand.Intn(len(globals.DataStoreIDGenerators))
|
||||
|
||||
dataStoreIDGenerator := globals.DataStoreIDGenerators[nodeID]
|
||||
|
||||
dataID := dataStoreIDGenerator.Next()
|
||||
database.SetDataStoreIDGeneratorLastID(nodeID, dataStoreIDGenerator.Value)
|
||||
database.InitializeCourseData(dataID, client.PID(), param.Size, param.Name, param.Flag, param.ExtraData, param.DataType, param.Period)
|
||||
|
||||
if param.DataType != 1 { // 1 is Mii data, assume other values are course meta data
|
||||
database.UpdateCourseMetaBinary(dataID, param.MetaBinary)
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("course/%d.bin", dataID)
|
||||
bucket := os.Getenv("S3_BUCKET_NAME")
|
||||
date := strconv.Itoa(int(time.Now().Unix()))
|
||||
pid := strconv.Itoa(int(client.PID()))
|
||||
|
||||
data := pid + bucket + key + date
|
||||
|
||||
hmac := hmac.New(sha256.New, globals.HMACSecret)
|
||||
hmac.Write([]byte(data))
|
||||
|
||||
signature := hex.EncodeToString(hmac.Sum(nil))
|
||||
|
||||
fieldBucket := datastore.NewDataStoreKeyValue()
|
||||
fieldBucket.Key = "bucket"
|
||||
fieldBucket.Value = bucket
|
||||
|
||||
fieldKey := datastore.NewDataStoreKeyValue()
|
||||
fieldKey.Key = "key"
|
||||
fieldKey.Value = key
|
||||
|
||||
fieldACL := datastore.NewDataStoreKeyValue()
|
||||
fieldACL.Key = "acl"
|
||||
fieldACL.Value = "private"
|
||||
|
||||
fieldPID := datastore.NewDataStoreKeyValue()
|
||||
fieldPID.Key = "pid"
|
||||
fieldPID.Value = pid
|
||||
|
||||
fieldDate := datastore.NewDataStoreKeyValue()
|
||||
fieldDate.Key = "date"
|
||||
fieldDate.Value = date
|
||||
|
||||
fieldSignature := datastore.NewDataStoreKeyValue()
|
||||
fieldSignature.Key = "signature"
|
||||
fieldSignature.Value = signature
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
|
||||
pReqPostInfo := datastore.NewDataStoreReqPostInfo()
|
||||
|
||||
pReqPostInfo.DataID = dataID
|
||||
pReqPostInfo.URL = os.Getenv("DATASTORE_UPLOAD_URL")
|
||||
pReqPostInfo.RequestHeaders = []*datastore.DataStoreKeyValue{}
|
||||
pReqPostInfo.FormFields = []*datastore.DataStoreKeyValue{fieldBucket, fieldKey, fieldACL, fieldPID, fieldDate, fieldSignature}
|
||||
pReqPostInfo.RootCACert = []byte{}
|
||||
|
||||
rmcResponseStream.WriteStructure(pReqPostInfo)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore.MethodPreparePostObject, rmcResponseBody)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
responsePacket, _ := nex.NewPacketV1(client, nil)
|
||||
|
||||
responsePacket.SetVersion(1)
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package nex_datastore
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func RateObjects(err error, client *nex.Client, callID uint32, targets []*datastore.DataStoreRatingTarget, params []*datastore.DataStoreRateObjectParam, transactional bool, fetchRatings bool) {
|
||||
// TODO: complete this
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
|
||||
rmcResponseStream.WriteUInt32LE(0x00000000) // pRatings List length 0
|
||||
rmcResponseStream.WriteUInt32LE(0x00000000) // pResults List length 0
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore.MethodRateObjects, rmcResponseBody)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
responsePacket, _ := nex.NewPacketV1(client, nil)
|
||||
|
||||
responsePacket.SetVersion(1)
|
||||
responsePacket.SetSource(0xA1)
|
||||
responsePacket.SetDestination(0xAF)
|
||||
responsePacket.SetType(nex.DataPacket)
|
||||
responsePacket.SetPayload(rmcResponseBytes)
|
||||
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
@@ -3,26 +3,62 @@ package nex_datastore_super_mario_maker
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func AddToBufferQueues(err error, client *nex.Client, callID uint32, params []*datastore_super_mario_maker.BufferQueueParam, buffers [][]byte) {
|
||||
pResults := make([]uint32, 0)
|
||||
|
||||
for i := 0; i < len(params); i++ {
|
||||
buffer := buffers[i]
|
||||
param := params[i]
|
||||
|
||||
database.InsertBufferQueueData(param.DataID, param.Slot, buffer)
|
||||
database.IncrementCourseFailCount(param.DataID)
|
||||
|
||||
pResults = append(pResults, 0x690001)
|
||||
func AddToBufferQueues(err error, packet nex.PacketInterface, callID uint32, params []*datastore_super_mario_maker_types.BufferQueueParam, buffers [][]byte) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
client := packet.Sender()
|
||||
|
||||
rmcResponseStream.WriteListUInt32LE(pResults)
|
||||
pResults := make([]*nex.Result, 0)
|
||||
|
||||
// * The number of params and buffers CAN be allowed
|
||||
// * to differ, though this doesn't appear to happen
|
||||
// * in normal gameplay. In these cases, the real
|
||||
// * server will just loop over smallest list and
|
||||
// * ignore all other data in the larger list
|
||||
iterations := min(len(params), len(buffers))
|
||||
|
||||
for i := 0; i < iterations; i++ {
|
||||
param := params[i]
|
||||
buffer := buffers[i]
|
||||
|
||||
if param.Slot == 0 {
|
||||
objectInfo, errCode := datastore_db.GetObjectInfoByDataID(param.DataID)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
// * Objects with DataType 1 are "maker" objects. When adding
|
||||
// * a buffer to slot 0 of a maker object, a course is being
|
||||
// * added to that users "Starred Courses" list. The buffer
|
||||
// * is the courses DataID saved as an 8 byte buffer. To
|
||||
// * prevent people from adding courses to random users
|
||||
// * "Starred Courses" lists, we have to verify the requesting
|
||||
// * client owns the maker object
|
||||
if objectInfo.DataType == 1 && objectInfo.OwnerID != client.PID() {
|
||||
return nex.Errors.DataStore.PermissionDenied
|
||||
}
|
||||
}
|
||||
|
||||
errCode := datastore_smm_db.InsertOrUpdateBufferQueueData(param.DataID, param.Slot, buffer)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
pResults = append(pResults, nex.NewResultSuccess(nex.Errors.Core.Unknown)) // * Seems to ALWAYS be a success?
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListResult(pResults)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
@@ -42,5 +78,7 @@ func AddToBufferQueues(err error, client *nex.Client, callID uint32, params []*d
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
package nex_datastore_super_mario_maker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func CheckRateCustomRankingCounter(err error, client *nex.Client, callID uint32, applicationID uint32) {
|
||||
var isBelowThreshold uint8
|
||||
|
||||
switch applicationID {
|
||||
case 0: // unknown
|
||||
isBelowThreshold = 1
|
||||
default:
|
||||
fmt.Printf("[Warning] DataStoreSMMProtocol::CheckRateCustomRankingCounter Unsupported applicationID: %v\n", applicationID)
|
||||
func CheckRateCustomRankingCounter(err error, packet nex.PacketInterface, callID uint32, applicationID uint32) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
client := packet.Sender()
|
||||
|
||||
rmcResponseStream.WriteUInt8(isBelowThreshold)
|
||||
// * No idea what this is. Only seen application ID 0
|
||||
// * used, and it's always true? Unsure what this checks
|
||||
isBelowThreshold := false
|
||||
|
||||
switch applicationID {
|
||||
case 0: // * Unknown
|
||||
isBelowThreshold = true
|
||||
default:
|
||||
globals.Logger.Warningf("Unsupported applicationID: %d", applicationID)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteBool(isBelowThreshold)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
@@ -40,5 +47,7 @@ func CheckRateCustomRankingCounter(err error, client *nex.Client, callID uint32,
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,19 +3,61 @@ package nex_datastore_super_mario_maker
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func CompleteAttachFile(err error, client *nex.Client, callID uint32, dataStoreCompletePostParam *datastore.DataStoreCompletePostParam) {
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
func CompleteAttachFile(err error, packet nex.PacketInterface, callID uint32, param *datastore_types.DataStoreCompletePostParam) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
// TODO: complete this
|
||||
client := packet.Sender()
|
||||
|
||||
rmcResponseStream.WriteString(fmt.Sprintf("http://%s.b-cdn.net/image/%d.jpg", os.Getenv("S3_BUCKET_NAME"), dataStoreCompletePostParam.DataID))
|
||||
// TODO - What is param.IsSuccess? Is this correct?
|
||||
if !param.IsSuccess {
|
||||
return nex.Errors.DataStore.InvalidArgument
|
||||
}
|
||||
|
||||
bucket := os.Getenv("PN_SMM_CONFIG_S3_BUCKET")
|
||||
key := fmt.Sprintf("%d.jpg", param.DataID)
|
||||
|
||||
objectSizeS3, err := globals.S3ObjectSize(bucket, key)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.NotFound
|
||||
}
|
||||
|
||||
objectSizeDB, errCode := datastore_db.GetObjectSizeDataID(param.DataID)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
if objectSizeS3 != uint64(objectSizeDB) {
|
||||
// TODO - Is this a good error?
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
errCode = datastore_db.UpdateObjectUploadCompletedByDataID(param.DataID, true)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
pURL, err := globals.Presigner.GetObject(bucket, key, time.Minute*15)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.OperationNotAllowed
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteString(pURL.String())
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
@@ -35,5 +77,7 @@ func CompleteAttachFile(err error, client *nex.Client, callID uint32, dataStoreC
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -2,25 +2,41 @@ package nex_datastore_super_mario_maker
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/utility"
|
||||
)
|
||||
|
||||
// This is the same as DataStoreSMM::RecommendedCourseSearchObject
|
||||
// Not sure why they used a different method here?
|
||||
func CTRPickUpCourseSearchObject(err error, client *nex.Client, callID uint32, dataStoreSearchParam *datastore.DataStoreSearchParam, extraData []string) {
|
||||
pRankingResults := make([]*datastore_super_mario_maker.DataStoreCustomRankingResult, 0)
|
||||
|
||||
courseMetadatas := database.GetCourseMetadatasByLimit(100) // In PCAPs param.minimalRatingFrequency is 100 but is 0 here?
|
||||
|
||||
for _, courseMetadata := range courseMetadatas {
|
||||
pRankingResults = append(pRankingResults, utility.CourseMetadataToDataStoreCustomRankingResult(courseMetadata))
|
||||
func CTRPickUpCourseSearchObject(err error, packet nex.PacketInterface, callID uint32, param *datastore_types.DataStoreSearchParam, extraData []string) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
client := packet.Sender()
|
||||
|
||||
// * This method is only used by the 3DS version
|
||||
// * of Super Mario Maker and is functionally
|
||||
// * identical to the WiiU versions
|
||||
// * DataStoreSMM::RecommendedCourseSearchObject
|
||||
// * method. The 3DS version LIKELY uses this as
|
||||
// * a way to filter out courses which use the
|
||||
// * Mystery Mushroom powerup, since it's not
|
||||
// * officially supported by the 3DS version. That
|
||||
// * said, the Mystery Mushroom IS still in the
|
||||
// * game and can be used somewhat normally, so
|
||||
// * no filtering is done here to prevent that.
|
||||
// * I'm not even sure how we would detect that
|
||||
|
||||
// TODO - Research extraData
|
||||
// TODO - Use the offet? Real client never uses it, but might be nice for completeness sake?
|
||||
pRankingResults, errCode := datastore_smm_db.GetRandomCoursesWithLimit(int(param.ResultRange.Length))
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListStructure(pRankingResults)
|
||||
|
||||
@@ -42,5 +58,7 @@ func CTRPickUpCourseSearchObject(err error, client *nex.Client, callID uint32, d
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -2,25 +2,70 @@ package nex_datastore_super_mario_maker
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/utility"
|
||||
)
|
||||
|
||||
func FollowingsLatestCourseSearchObject(err error, client *nex.Client, callID uint32, dataStoreSearchParam *datastore.DataStoreSearchParam, extraData []string) {
|
||||
pRankingResults := make([]*datastore_super_mario_maker.DataStoreCustomRankingResult, 0)
|
||||
func FollowingsLatestCourseSearchObject(err error, packet nex.PacketInterface, callID uint32, param *datastore_types.DataStoreSearchParam, extraData []string) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
for _, pid := range dataStoreSearchParam.OwnerIds {
|
||||
courseMetadatas := database.GetCourseMetadatasByPID(pid)
|
||||
client := packet.Sender()
|
||||
|
||||
for _, courseMetadata := range courseMetadatas {
|
||||
pRankingResults = append(pRankingResults, utility.CourseMetadataToDataStoreCustomRankingResult(courseMetadata))
|
||||
pRankingResults := make([]*datastore_super_mario_maker_types.DataStoreCustomRankingResult, 0)
|
||||
|
||||
// * This seems to ONLY be used to get rankings for course objects
|
||||
// * uploaded by the users in param.OwnerIDs? If param.ResultOption
|
||||
// * contains the flag 0x20 ("return scores"), then the real server
|
||||
// * does some kind of check over extraData. It's unknown what this
|
||||
// * check is, so it's not done here. All other data in param seems
|
||||
// * to be unused here.
|
||||
for _, pid := range param.OwnerIDs {
|
||||
courseObjectIDs, errCode := datastore_smm_db.GetUserCourseObjectIDs(pid)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
// * This method seems to always use slot 0?
|
||||
results := datastore_smm_db.GetCustomRankingsByDataIDs(0, courseObjectIDs)
|
||||
|
||||
for _, rankingResult := range results {
|
||||
// * This is kind of backwards.
|
||||
// * The database pulls this data
|
||||
// * by default, so it can be done
|
||||
// * in a single query. So instead
|
||||
// * of checking if a flag *IS*
|
||||
// * set, and conditionally *ADDING*
|
||||
// * the fields, we check if a flag
|
||||
// * is *NOT* set and conditionally
|
||||
// * *REMOVE* the field
|
||||
if param.ResultOption&0x1 == 0 {
|
||||
rankingResult.MetaInfo.Tags = make([]string, 0)
|
||||
}
|
||||
|
||||
if param.ResultOption&0x2 == 0 {
|
||||
rankingResult.MetaInfo.Ratings = make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
}
|
||||
|
||||
if param.ResultOption&0x4 == 0 {
|
||||
rankingResult.MetaInfo.MetaBinary = make([]byte, 0)
|
||||
}
|
||||
|
||||
// TODO - If this flag is set, extraData is checked somehow
|
||||
if param.ResultOption&0x20 == 0 {
|
||||
rankingResult.Score = 0
|
||||
}
|
||||
|
||||
pRankingResults = append(pRankingResults, rankingResult)
|
||||
}
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListStructure(pRankingResults)
|
||||
|
||||
@@ -42,5 +87,7 @@ func FollowingsLatestCourseSearchObject(err error, client *nex.Client, callID ui
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -8,26 +8,38 @@ import (
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
var MAX_COURSE_UPLOADS uint32 = 0x64 // Nintendo has this set to 100, but we can change it if we want
|
||||
// * Nintendo sets this to 10 by default
|
||||
// * and users earn more upload slots up
|
||||
// * to 100.
|
||||
// * This is a stupid, unfun, mechanic so
|
||||
// * everyone gets 100 by default. Can be
|
||||
// * more, but 100 is fine tbh
|
||||
var MAX_COURSE_UPLOADS uint32 = 100
|
||||
|
||||
func GetApplicationConfig(err error, client *nex.Client, callID uint32, applicationID uint32) {
|
||||
func GetApplicationConfig(err error, packet nex.PacketInterface, callID uint32, applicationID uint32) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
client := packet.Sender()
|
||||
|
||||
config := make([]uint32, 0)
|
||||
|
||||
switch applicationID {
|
||||
case 0: // Unknown?
|
||||
config = getApplicationConfig_Unknown0()
|
||||
case 1: // Official player PIDs
|
||||
config = getApplicationConfig_OfficialPlayers()
|
||||
case 2: // Unknown?
|
||||
case 0: // * Player config?
|
||||
config = getApplicationConfig_PlayerConfig()
|
||||
case 1: // * PIDs of the "Official" makers in the "MAKERS" section
|
||||
config = getApplicationConfig_OfficialMakers()
|
||||
case 2: // * Unknown
|
||||
config = getApplicationConfig_Unknown2()
|
||||
case 10: // Unknown?
|
||||
case 10: // * Unknown
|
||||
config = getApplicationConfig_Unknown10()
|
||||
default:
|
||||
fmt.Printf("[Warning] DataStoreSMMProtocol::GetApplicationConfig Unsupported applicationID: %v\n", applicationID)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListUInt32LE(config)
|
||||
|
||||
@@ -49,12 +61,17 @@ func GetApplicationConfig(err error, client *nex.Client, callID uint32, applicat
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func getApplicationConfig_Unknown0() []uint32 {
|
||||
// I have no idea what this is
|
||||
// Just replaying data sent from the real server
|
||||
func getApplicationConfig_PlayerConfig() []uint32 {
|
||||
// * This seems to be per-user configuration
|
||||
// * settings, based on the fact that the
|
||||
// * number of max uploads a user can do is
|
||||
// * sent here. No idea what anything else
|
||||
// * means
|
||||
return []uint32{
|
||||
0x01000000, 0x32000000, 0x96000000, 0x2c010000, 0xf4010000,
|
||||
0x20030000, 0x14050000, 0xd0070000, 0xb80b0000, 0x88130000,
|
||||
@@ -67,29 +84,30 @@ func getApplicationConfig_Unknown0() []uint32 {
|
||||
}
|
||||
}
|
||||
|
||||
func getApplicationConfig_OfficialPlayers() []uint32 {
|
||||
func getApplicationConfig_OfficialMakers() []uint32 {
|
||||
// * Used as the PIDs for the "Official" makers in the "MAKERS" section
|
||||
return []uint32{
|
||||
0x02000000, // 2 (not a real user PID, this translates to the internal Quazal Rendez-Vous user used by NEX)
|
||||
0x70cc8269, // 1770179696 (official_player0 on NN, need to make PN versions)
|
||||
0x50cc8269, // 1770179664 (official_player1 on NN, need to make PN versions)
|
||||
0x38cc8269, // 1770179640 (official_player2 on NN, need to make PN versions)
|
||||
0xdbd08269, // 1770180827 (official_player3 on NN, need to make PN versions)
|
||||
0xa9d08269, // 1770180777 (official_player4 on NN, need to make PN versions)
|
||||
0x89d08269, // 1770180745 (official_player5 on NN, need to make PN versions)
|
||||
0x59c48269, // 1770177625 (official_player6 on NN, need to make PN versions)
|
||||
0x36c48269, // 1770177590 (official_player7 on NN, need to make PN versions)
|
||||
0x02000000, // * 2 (not a real user PID, this translates to the internal Quazal Rendez-Vous user used by NEX)
|
||||
0x70cc8269, // * 1770179696 (official_player0 on NN, need to make PN versions)
|
||||
0x50cc8269, // * 1770179664 (official_player1 on NN, need to make PN versions)
|
||||
0x38cc8269, // * 1770179640 (official_player2 on NN, need to make PN versions)
|
||||
0xdbd08269, // * 1770180827 (official_player3 on NN, need to make PN versions)
|
||||
0xa9d08269, // * 1770180777 (official_player4 on NN, need to make PN versions)
|
||||
0x89d08269, // * 1770180745 (official_player5 on NN, need to make PN versions)
|
||||
0x59c48269, // * 1770177625 (official_player6 on NN, need to make PN versions)
|
||||
0x36c48269, // * 1770177590 (official_player7 on NN, need to make PN versions)
|
||||
}
|
||||
}
|
||||
|
||||
func getApplicationConfig_Unknown2() []uint32 {
|
||||
// I have no idea what this is
|
||||
// Just replaying data sent from the real server
|
||||
// * I have no idea what this is
|
||||
// * Just replaying data sent from the real server
|
||||
return []uint32{0xdf070000, 0x0c000000, 0x16000000, 0x05000000, 0x00000000}
|
||||
}
|
||||
|
||||
func getApplicationConfig_Unknown10() []uint32 {
|
||||
// I have no idea what this is
|
||||
// Just replaying data sent from the real server
|
||||
// Only seen on the 3DS
|
||||
// * I have no idea what this is
|
||||
// * Just replaying data sent from the real server
|
||||
// * Only seen on the 3DS
|
||||
return []uint32{35, 75, 96, 40, 5, 6}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,15 @@ import (
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetApplicationConfigString(err error, client *nex.Client, callID uint32, applicationID uint32) {
|
||||
// Word blacklists?
|
||||
func GetApplicationConfigString(err error, packet nex.PacketInterface, callID uint32, applicationID uint32) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
client := packet.Sender()
|
||||
|
||||
// * Word blacklists?
|
||||
config := make([]string, 0)
|
||||
|
||||
switch applicationID {
|
||||
@@ -23,7 +30,7 @@ func GetApplicationConfigString(err error, client *nex.Client, callID uint32, ap
|
||||
fmt.Printf("[Warning] DataStoreSMMProtocol::GetApplicationConfigString Unsupported applicationID: %v\n", applicationID)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListString(config)
|
||||
|
||||
@@ -45,11 +52,14 @@ func GetApplicationConfigString(err error, client *nex.Client, callID uint32, ap
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func getApplicationConfigString_WordBlacklist1() []string {
|
||||
// Just replaying data sent from Nintendo's servers
|
||||
// * Just replaying data sent from Nintendo's servers
|
||||
// * Please no cancel for swears/slurs ;-;
|
||||
return []string{
|
||||
"けされ", "消され", "削除され", "リセットされ",
|
||||
"BANされ", "BANされ", "キミのコース", "君のコース",
|
||||
@@ -72,14 +82,16 @@ func getApplicationConfigString_WordBlacklist1() []string {
|
||||
}
|
||||
|
||||
func getApplicationConfigString_WordBlacklist2() []string {
|
||||
// Just replaying data sent from Nintendo's servers
|
||||
// * Just replaying data sent from Nintendo's servers
|
||||
// * Please no cancel for swears/slurs ;-;
|
||||
return []string{
|
||||
"ゼロから", "0から", "0から", "い い ね", "いい", "東日本", "大震",
|
||||
}
|
||||
}
|
||||
|
||||
func getApplicationConfigString_WordBlacklist3() []string {
|
||||
// Just replaying data sent from Nintendo's servers
|
||||
// * Just replaying data sent from Nintendo's servers
|
||||
// * Please no cancel for swears/slurs ;-;
|
||||
return []string{
|
||||
"いいね", "下さい", "ください",
|
||||
"押して", "おして", "返す",
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
package nex_datastore_super_mario_maker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetBufferQueue(err error, client *nex.Client, callID uint32, param *datastore_super_mario_maker.BufferQueueParam) {
|
||||
// TODO: complete this
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
|
||||
var pBufferQueue [][]byte
|
||||
|
||||
switch param.Slot {
|
||||
case 0: // unknown
|
||||
pBufferQueue = make([][]byte, 0)
|
||||
case 3: // death data
|
||||
pBufferQueue = database.GetBufferQueueDeathData(param.DataID)
|
||||
default:
|
||||
fmt.Printf("[Warning] DataStoreSMMProtocol::GetBufferQueue Unsupported slot: %v\n", param.Slot)
|
||||
func GetBufferQueue(err error, packet nex.PacketInterface, callID uint32, param *datastore_super_mario_maker_types.BufferQueueParam) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
client := packet.Sender()
|
||||
|
||||
pBufferQueue, errCode := datastore_smm_db.GetBufferQueuesByDataIDAndSlot(param.DataID, param.Slot)
|
||||
if errCode != 0 {
|
||||
globals.Logger.Errorf("Error code %d for object %d", errCode, param.DataID)
|
||||
return errCode
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
rmcResponseStream.WriteListQBuffer(pBufferQueue)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
@@ -45,5 +43,7 @@ func GetBufferQueue(err error, client *nex.Client, callID uint32, param *datasto
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,34 +3,32 @@ package nex_datastore_super_mario_maker
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetCourseRecord(err error, client *nex.Client, callID uint32, param *datastore_super_mario_maker.DataStoreGetCourseRecordParam) {
|
||||
worldRecord := database.GetCourseWorldRecord(param.DataID)
|
||||
func GetCourseRecord(err error, packet nex.PacketInterface, callID uint32, param *datastore_super_mario_maker_types.DataStoreGetCourseRecordParam) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
client := packet.Sender()
|
||||
|
||||
result, errCode := datastore_smm_db.GetCourseRecordByDataIDAndSlot(param.DataID, param.Slot)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteStructure(result)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore_super_mario_maker.ProtocolID, callID)
|
||||
|
||||
if worldRecord == nil {
|
||||
rmcResponse.SetError(0x690004)
|
||||
} else {
|
||||
result := datastore_super_mario_maker.NewDataStoreGetCourseRecordResult()
|
||||
result.DataID = param.DataID
|
||||
result.Slot = param.Slot
|
||||
result.FirstPID = worldRecord.FirstPID
|
||||
result.BestPID = worldRecord.BestPID
|
||||
result.BestScore = worldRecord.Score
|
||||
result.CreatedTime = worldRecord.CreatedTime
|
||||
result.UpdatedTime = worldRecord.UpdatedTime
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
|
||||
rmcResponseStream.WriteStructure(result)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
rmcResponse.SetSuccess(datastore_super_mario_maker.MethodGetCourseRecord, rmcResponseBody)
|
||||
}
|
||||
rmcResponse.SetSuccess(datastore_super_mario_maker.MethodGetCourseRecord, rmcResponseBody)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
@@ -45,5 +43,7 @@ func GetCourseRecord(err error, client *nex.Client, callID uint32, param *datast
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,37 +3,64 @@ package nex_datastore_super_mario_maker
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/utility"
|
||||
)
|
||||
|
||||
func GetCustomRankingByDataId(err error, client *nex.Client, callID uint32, param *datastore_super_mario_maker.DataStoreGetCustomRankingByDataIdParam) {
|
||||
var pRankingResult []*datastore_super_mario_maker.DataStoreCustomRankingResult
|
||||
var pResults []uint32
|
||||
|
||||
switch param.ApplicationId {
|
||||
case 0:
|
||||
if len(param.DataIdList) == 0 { // Starred courses
|
||||
pRankingResult, pResults = getCustomRankingByDataIdStarredCourses(client.PID())
|
||||
} else { // Played courses
|
||||
pRankingResult, pResults = getCustomRankingByDataIdCourseMetadata(param)
|
||||
}
|
||||
case 300000000: // Mii data
|
||||
pRankingResult, pResults = getCustomRankingByDataIdMiiData(param)
|
||||
default: // Normal metadata
|
||||
pRankingResult, pResults = getCustomRankingByDataIdCourseMetadata(param)
|
||||
func GetCustomRankingByDataID(err error, packet nex.PacketInterface, callID uint32, param *datastore_super_mario_maker_types.DataStoreGetCustomRankingByDataIDParam) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
client := packet.Sender()
|
||||
|
||||
pRankingResult := datastore_smm_db.GetCustomRankingsByDataIDs(param.ApplicationID, param.DataIDList)
|
||||
pResults := make([]*nex.Result, 0, len(param.DataIDList))
|
||||
|
||||
for _, rankingResult := range pRankingResult {
|
||||
// * This is kind of backwards.
|
||||
// * The database pulls this data
|
||||
// * by default, so it can be done
|
||||
// * in a single query. So instead
|
||||
// * of checking if a flag *IS*
|
||||
// * set, and conditionally *ADDING*
|
||||
// * the fields, we check if a flag
|
||||
// * is *NOT* set and conditionally
|
||||
// * *REMOVE* the field
|
||||
if param.ResultOption&0x1 == 0 {
|
||||
rankingResult.MetaInfo.Tags = make([]string, 0)
|
||||
}
|
||||
|
||||
if param.ResultOption&0x2 == 0 {
|
||||
rankingResult.MetaInfo.Ratings = make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
}
|
||||
|
||||
if param.ResultOption&0x4 == 0 {
|
||||
rankingResult.MetaInfo.MetaBinary = make([]byte, 0)
|
||||
}
|
||||
|
||||
if param.ResultOption&0x20 == 0 {
|
||||
rankingResult.Score = 0
|
||||
}
|
||||
|
||||
// * Since all errors are thrown away in
|
||||
// * datastore_smm_db.GetCustomRankingsByDataIDs
|
||||
// * assume all objects returned were a success
|
||||
pResults = append(pResults, nex.NewResultSuccess(nex.Errors.Core.Unknown))
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListStructure(pRankingResult)
|
||||
rmcResponseStream.WriteListUInt32LE(pResults)
|
||||
rmcResponseStream.WriteListResult(pResults)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore_super_mario_maker.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore_super_mario_maker.MethodGetCustomRankingByDataId, rmcResponseBody)
|
||||
rmcResponse.SetSuccess(datastore_super_mario_maker.MethodGetCustomRankingByDataID, rmcResponseBody)
|
||||
|
||||
rmcResponseBytes := rmcResponse.Bytes()
|
||||
|
||||
@@ -48,50 +75,7 @@ func GetCustomRankingByDataId(err error, client *nex.Client, callID uint32, para
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
}
|
||||
|
||||
func getCustomRankingByDataIdStarredCourses(pid uint32) ([]*datastore_super_mario_maker.DataStoreCustomRankingResult, []uint32) {
|
||||
courseMetadatas := database.GetUserStarredCourses(pid)
|
||||
|
||||
pRankingResult := make([]*datastore_super_mario_maker.DataStoreCustomRankingResult, 0)
|
||||
pResults := make([]uint32, 0)
|
||||
|
||||
for _, courseMetadata := range courseMetadatas {
|
||||
pRankingResult = append(pRankingResult, utility.CourseMetadataToDataStoreCustomRankingResult(courseMetadata))
|
||||
pResults = append(pResults, 0x690001)
|
||||
}
|
||||
|
||||
return pRankingResult, pResults
|
||||
}
|
||||
|
||||
func getCustomRankingByDataIdMiiData(param *datastore_super_mario_maker.DataStoreGetCustomRankingByDataIdParam) ([]*datastore_super_mario_maker.DataStoreCustomRankingResult, []uint32) {
|
||||
pRankingResult := make([]*datastore_super_mario_maker.DataStoreCustomRankingResult, 0)
|
||||
pResults := make([]uint32, 0)
|
||||
|
||||
for _, pid := range param.DataIdList {
|
||||
pid := uint32(pid)
|
||||
miiInfo := database.GetUserMiiInfoByPID(pid) // This isn't actually a PID when using the official servers! I set it as one to make this easier for me
|
||||
|
||||
if miiInfo != nil {
|
||||
pRankingResult = append(pRankingResult, utility.UserMiiDataToDataStoreCustomRankingResult(pid, miiInfo))
|
||||
pResults = append(pResults, 0x690001)
|
||||
}
|
||||
}
|
||||
|
||||
return pRankingResult, pResults
|
||||
}
|
||||
|
||||
func getCustomRankingByDataIdCourseMetadata(param *datastore_super_mario_maker.DataStoreGetCustomRankingByDataIdParam) ([]*datastore_super_mario_maker.DataStoreCustomRankingResult, []uint32) {
|
||||
courseMetadatas := database.GetCourseMetadataByDataIDs(param.DataIdList)
|
||||
|
||||
pRankingResult := make([]*datastore_super_mario_maker.DataStoreCustomRankingResult, 0)
|
||||
pResults := make([]uint32, 0)
|
||||
|
||||
for _, courseMetadata := range courseMetadatas {
|
||||
pRankingResult = append(pRankingResult, utility.CourseMetadataToDataStoreCustomRankingResult(courseMetadata))
|
||||
pResults = append(pResults, 0x690001)
|
||||
}
|
||||
|
||||
return pRankingResult, pResults
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -6,18 +6,29 @@ import (
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetDeletionReason(err error, client *nex.Client, callID uint32, dataIdLst []uint64) {
|
||||
// TODO: Complete this
|
||||
|
||||
pDeletionReasons := make([]uint32, 0)
|
||||
|
||||
for range dataIdLst {
|
||||
// We don't need the values right now
|
||||
// stub this
|
||||
pDeletionReasons = append(pDeletionReasons, 0x690007)
|
||||
func GetDeletionReason(err error, packet nex.PacketInterface, callID uint32, dataIDLst []uint64) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
client := packet.Sender()
|
||||
|
||||
// TODO - Complete this
|
||||
// * It's not actually known what the
|
||||
// * real "deletion reason" values are.
|
||||
// * This is stubbed until we figure
|
||||
// * that out
|
||||
pDeletionReasons := make([]uint32, 0)
|
||||
|
||||
for range dataIDLst {
|
||||
// * Every course I've checked has had this
|
||||
// * set to 0, even if the course is not
|
||||
// * deleted
|
||||
pDeletionReasons = append(pDeletionReasons, 0)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListUInt32LE(pDeletionReasons)
|
||||
|
||||
@@ -39,5 +50,7 @@ func GetDeletionReason(err error, client *nex.Client, callID uint32, dataIdLst [
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -2,19 +2,140 @@ package nex_datastore_super_mario_maker
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetMetasWithCourseRecord(err error, client *nex.Client, callID uint32, dataStoreGetCourseRecordParams []*datastore_super_mario_maker.DataStoreGetCourseRecordParam, dataStoreGetMetaParam *datastore.DataStoreGetMetaParam) {
|
||||
// TODO: complete this
|
||||
func GetMetasWithCourseRecord(err error, packet nex.PacketInterface, callID uint32, params []*datastore_super_mario_maker_types.DataStoreGetCourseRecordParam, metaParam *datastore_types.DataStoreGetMetaParam) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
client := packet.Sender()
|
||||
|
||||
rmcResponseStream.WriteUInt32LE(0x00000000) // pMetaInfo List length 0
|
||||
rmcResponseStream.WriteUInt32LE(0x00000000) // pCourseResults List length 0
|
||||
rmcResponseStream.WriteUInt32LE(0x00000000) // pResults List length 0
|
||||
// * The functionality here is largely a guess
|
||||
// * based on how GetCourseRecord works and
|
||||
// * based on testing with a custom client.
|
||||
// * During normal gameplay this method never
|
||||
// * seems to send any parameters, though if a
|
||||
// * custom client is used it IS functional
|
||||
|
||||
pMetaInfo := make([]*datastore_types.DataStoreMetaInfo, 0, len(params))
|
||||
pCourseResults := make([]*datastore_super_mario_maker_types.DataStoreGetCourseRecordResult, 0, len(params))
|
||||
pResults := make([]*nex.Result, 0, len(params))
|
||||
|
||||
for _, param := range params {
|
||||
// * metaParam has a password, but it's always set to 0.
|
||||
// * It also wouldn't make much sense for the same password
|
||||
// * to be used for all objects being requested here. So
|
||||
// * just assume metaParam is ONLY used for the resultOption
|
||||
// * field?
|
||||
objectInfo, errCode := datastore_db.GetObjectInfoByDataID(param.DataID)
|
||||
if errCode != 0 {
|
||||
// TODO - Maybe this should be broken out into a util function in globals?
|
||||
objectInfo = datastore_types.NewDataStoreMetaInfo()
|
||||
objectInfo.DataID = 0
|
||||
objectInfo.OwnerID = 0
|
||||
objectInfo.Size = 0
|
||||
objectInfo.Name = ""
|
||||
objectInfo.DataType = 0
|
||||
objectInfo.MetaBinary = []byte{}
|
||||
objectInfo.Permission = datastore_types.NewDataStorePermission()
|
||||
objectInfo.Permission.Permission = 0
|
||||
objectInfo.Permission.RecipientIDs = []uint32{}
|
||||
objectInfo.DelPermission = datastore_types.NewDataStorePermission()
|
||||
objectInfo.DelPermission.Permission = 0
|
||||
objectInfo.DelPermission.RecipientIDs = []uint32{}
|
||||
objectInfo.CreatedTime = nex.NewDateTime(0)
|
||||
objectInfo.UpdatedTime = nex.NewDateTime(0)
|
||||
objectInfo.Period = 0
|
||||
objectInfo.Status = 0
|
||||
objectInfo.ReferredCnt = 0
|
||||
objectInfo.ReferDataID = 0
|
||||
objectInfo.Flag = 0
|
||||
objectInfo.ReferredTime = nex.NewDateTime(0)
|
||||
objectInfo.ExpireTime = nex.NewDateTime(0)
|
||||
objectInfo.Tags = []string{}
|
||||
objectInfo.Ratings = []*datastore_types.DataStoreRatingInfoWithSlot{}
|
||||
} else {
|
||||
errCode = globals.VerifyObjectPermission(objectInfo.OwnerID, client.PID(), objectInfo.Permission)
|
||||
if errCode != 0 {
|
||||
objectInfo = datastore_types.NewDataStoreMetaInfo()
|
||||
objectInfo.DataID = 0
|
||||
objectInfo.OwnerID = 0
|
||||
objectInfo.Size = 0
|
||||
objectInfo.Name = ""
|
||||
objectInfo.DataType = 0
|
||||
objectInfo.MetaBinary = []byte{}
|
||||
objectInfo.Permission = datastore_types.NewDataStorePermission()
|
||||
objectInfo.Permission.Permission = 0
|
||||
objectInfo.Permission.RecipientIDs = []uint32{}
|
||||
objectInfo.DelPermission = datastore_types.NewDataStorePermission()
|
||||
objectInfo.DelPermission.Permission = 0
|
||||
objectInfo.DelPermission.RecipientIDs = []uint32{}
|
||||
objectInfo.CreatedTime = nex.NewDateTime(0)
|
||||
objectInfo.UpdatedTime = nex.NewDateTime(0)
|
||||
objectInfo.Period = 0
|
||||
objectInfo.Status = 0
|
||||
objectInfo.ReferredCnt = 0
|
||||
objectInfo.ReferDataID = 0
|
||||
objectInfo.Flag = 0
|
||||
objectInfo.ReferredTime = nex.NewDateTime(0)
|
||||
objectInfo.ExpireTime = nex.NewDateTime(0)
|
||||
objectInfo.Tags = []string{}
|
||||
objectInfo.Ratings = []*datastore_types.DataStoreRatingInfoWithSlot{}
|
||||
}
|
||||
|
||||
// * This is kind of backwards.
|
||||
// * The database pulls this data
|
||||
// * by default, so it can be done
|
||||
// * in a single query. So instead
|
||||
// * of checking if a flag *IS*
|
||||
// * set, and conditionally *ADDING*
|
||||
// * the fields, we check if a flag
|
||||
// * is *NOT* set and conditionally
|
||||
// * *REMOVE* the field
|
||||
if metaParam.ResultOption&0x1 == 0 {
|
||||
objectInfo.Tags = make([]string, 0)
|
||||
}
|
||||
|
||||
if metaParam.ResultOption&0x2 == 0 {
|
||||
objectInfo.Ratings = make([]*datastore_types.DataStoreRatingInfoWithSlot, 0)
|
||||
}
|
||||
|
||||
if metaParam.ResultOption&0x4 == 0 {
|
||||
objectInfo.MetaBinary = make([]byte, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// * Ignore errors, real server sends empty struct if can't be found
|
||||
courseRecord, errCode := datastore_smm_db.GetCourseRecordByDataIDAndSlot(param.DataID, param.Slot)
|
||||
if errCode != 0 || objectInfo.DataID == 0 { // * DataID == 0 means could not be found or accessed
|
||||
courseRecord = datastore_super_mario_maker_types.NewDataStoreGetCourseRecordResult()
|
||||
courseRecord.DataID = 0
|
||||
courseRecord.Slot = 0
|
||||
courseRecord.FirstPID = 0
|
||||
courseRecord.BestPID = 0
|
||||
courseRecord.BestScore = 0
|
||||
courseRecord.CreatedTime = nex.NewDateTime(0)
|
||||
courseRecord.UpdatedTime = nex.NewDateTime(0)
|
||||
}
|
||||
|
||||
pMetaInfo = append(pMetaInfo, objectInfo)
|
||||
pCourseResults = append(pCourseResults, courseRecord)
|
||||
pResults = append(pResults, nex.NewResultSuccess(nex.Errors.Core.Unknown)) // * Real server ALWAYS returns a success
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListStructure(pMetaInfo)
|
||||
rmcResponseStream.WriteListStructure(pCourseResults)
|
||||
rmcResponseStream.WriteListResult(pResults)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
|
||||
@@ -34,5 +155,7 @@ func GetMetasWithCourseRecord(err error, client *nex.Client, callID uint32, data
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,33 +3,54 @@ package nex_datastore_super_mario_maker
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func GetObjectInfos(err error, client *nex.Client, callID uint32, dataIDs []uint64) {
|
||||
pInfos := make([]*datastore_super_mario_maker.DataStoreFileServerObjectInfo, 0)
|
||||
func GetObjectInfos(err error, packet nex.PacketInterface, callID uint32, dataIDs []uint64) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
courseMetadatas := database.GetCourseMetadataByDataIDs(dataIDs)
|
||||
client := packet.Sender()
|
||||
|
||||
for _, courseMetadata := range courseMetadatas {
|
||||
info := datastore_super_mario_maker.NewDataStoreFileServerObjectInfo()
|
||||
info.DataID = courseMetadata.DataID
|
||||
info.GetInfo = datastore.NewDataStoreReqGetInfo()
|
||||
info.GetInfo.URL = fmt.Sprintf("http://%s.b-cdn.net/course/%d.bin", os.Getenv("S3_BUCKET_NAME"), courseMetadata.DataID)
|
||||
info.GetInfo.RequestHeaders = []*datastore.DataStoreKeyValue{}
|
||||
info.GetInfo.Size = courseMetadata.Size
|
||||
info.GetInfo.RootCA = []byte{}
|
||||
info.GetInfo.DataID = courseMetadata.DataID
|
||||
pInfos := make([]*datastore_super_mario_maker_types.DataStoreFileServerObjectInfo, 0)
|
||||
|
||||
for _, dataID := range dataIDs {
|
||||
objectInfo, errCode := datastore_db.GetObjectInfoByDataID(dataID)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
bucket := os.Getenv("PN_SMM_CONFIG_S3_BUCKET")
|
||||
key := fmt.Sprintf("%d.bin", objectInfo.DataID)
|
||||
|
||||
URL, err := globals.Presigner.GetObject(bucket, key, time.Minute*15)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.OperationNotAllowed
|
||||
}
|
||||
|
||||
info := datastore_super_mario_maker_types.NewDataStoreFileServerObjectInfo()
|
||||
info.DataID = objectInfo.DataID
|
||||
info.GetInfo = datastore_types.NewDataStoreReqGetInfo()
|
||||
info.GetInfo.URL = URL.String()
|
||||
info.GetInfo.RequestHeaders = []*datastore_types.DataStoreKeyValue{}
|
||||
info.GetInfo.Size = objectInfo.Size
|
||||
info.GetInfo.RootCACert = []byte{}
|
||||
info.GetInfo.DataID = objectInfo.DataID
|
||||
|
||||
pInfos = append(pInfos, info)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListStructure(pInfos)
|
||||
|
||||
@@ -51,5 +72,7 @@ func GetObjectInfos(err error, client *nex.Client, callID uint32, dataIDs []uint
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,71 +1,75 @@
|
||||
package nex_datastore_super_mario_maker
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func PrepareAttachFile(err error, client *nex.Client, callID uint32, dataStoreAttachFileParam *datastore_super_mario_maker.DataStoreAttachFileParam) {
|
||||
key := fmt.Sprintf("image/%d.jpg", dataStoreAttachFileParam.ReferDataID)
|
||||
bucket := os.Getenv("S3_BUCKET_NAME")
|
||||
date := strconv.Itoa(int(time.Now().Unix()))
|
||||
pid := strconv.Itoa(int(client.PID()))
|
||||
func PrepareAttachFile(err error, packet nex.PacketInterface, callID uint32, param *datastore_super_mario_maker_types.DataStoreAttachFileParam) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
data := pid + bucket + key + date
|
||||
client := packet.Sender()
|
||||
|
||||
hmac := hmac.New(sha256.New, globals.HMACSecret)
|
||||
hmac.Write([]byte(data))
|
||||
// * This method seems to be only used at "attach"
|
||||
// * a file to an existing object. In practice,
|
||||
// * SMM will use this to upload a courses preview
|
||||
// * image after uploading the course.
|
||||
// * param.ReferDataID is the courses object DataID
|
||||
|
||||
signature := hex.EncodeToString(hmac.Sum(nil))
|
||||
dataID, errCode := datastore_smm_db.InitializeObjectByAttachFileParam(client.PID(), param)
|
||||
if errCode != 0 {
|
||||
globals.Logger.Errorf("Error code %d on object init", errCode)
|
||||
return errCode
|
||||
}
|
||||
|
||||
fieldBucket := datastore.NewDataStoreKeyValue()
|
||||
fieldBucket.Key = "bucket"
|
||||
fieldBucket.Value = bucket
|
||||
// TODO - Should this be moved to InitializeObjectByAttachFileParam?
|
||||
// * This never seems to have any values during normal gameplay,
|
||||
// * but just in case
|
||||
for _, ratingInitParamWithSlot := range param.PostParam.RatingInitParams {
|
||||
errCode = datastore_db.InitializeObjectRatingWithSlot(dataID, ratingInitParamWithSlot)
|
||||
if errCode != 0 {
|
||||
globals.Logger.Errorf("Error code %d on rating init", errCode)
|
||||
return errCode
|
||||
}
|
||||
}
|
||||
|
||||
fieldKey := datastore.NewDataStoreKeyValue()
|
||||
fieldKey.Key = "key"
|
||||
fieldKey.Value = key
|
||||
// TODO - Check param.ContentType? Always seems to be "image/jpeg" but just in case?
|
||||
bucket := os.Getenv("PN_SMM_CONFIG_S3_BUCKET")
|
||||
key := fmt.Sprintf("%d.jpg", dataID)
|
||||
|
||||
fieldACL := datastore.NewDataStoreKeyValue()
|
||||
fieldACL.Key = "acl"
|
||||
fieldACL.Value = "public-read"
|
||||
// TODO - Should this also take in the param.ContentType? To add it to the policy?
|
||||
URL, formData, _ := globals.Presigner.PostObject(bucket, key, time.Minute*15)
|
||||
|
||||
fieldContentType := datastore.NewDataStoreKeyValue()
|
||||
fieldContentType.Key = "content-type"
|
||||
fieldContentType.Value = "image/jpeg"
|
||||
pReqPostInfo := datastore_types.NewDataStoreReqPostInfo()
|
||||
|
||||
fieldPID := datastore.NewDataStoreKeyValue()
|
||||
fieldPID.Key = "pid"
|
||||
fieldPID.Value = pid
|
||||
|
||||
fieldDate := datastore.NewDataStoreKeyValue()
|
||||
fieldDate.Key = "date"
|
||||
fieldDate.Value = date
|
||||
|
||||
fieldSignature := datastore.NewDataStoreKeyValue()
|
||||
fieldSignature.Key = "signature"
|
||||
fieldSignature.Value = signature
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
|
||||
pReqPostInfo := datastore.NewDataStoreReqPostInfo()
|
||||
|
||||
pReqPostInfo.DataID = dataStoreAttachFileParam.ReferDataID
|
||||
pReqPostInfo.URL = os.Getenv("DATASTORE_UPLOAD_URL")
|
||||
pReqPostInfo.RequestHeaders = []*datastore.DataStoreKeyValue{}
|
||||
pReqPostInfo.FormFields = []*datastore.DataStoreKeyValue{fieldBucket, fieldKey, fieldACL, fieldContentType, fieldPID, fieldDate, fieldSignature}
|
||||
pReqPostInfo.DataID = dataID
|
||||
pReqPostInfo.URL = URL.String()
|
||||
pReqPostInfo.RequestHeaders = []*datastore_types.DataStoreKeyValue{}
|
||||
pReqPostInfo.FormFields = make([]*datastore_types.DataStoreKeyValue, 0, len(formData))
|
||||
pReqPostInfo.RootCACert = []byte{}
|
||||
|
||||
for key, value := range formData {
|
||||
field := datastore_types.NewDataStoreKeyValue()
|
||||
field.Key = key
|
||||
field.Value = value
|
||||
|
||||
pReqPostInfo.FormFields = append(pReqPostInfo.FormFields, field)
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteStructure(pReqPostInfo)
|
||||
|
||||
rmcResponseBody := rmcResponseStream.Bytes()
|
||||
@@ -86,5 +90,7 @@ func PrepareAttachFile(err error, client *nex.Client, callID uint32, dataStoreAt
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,92 +3,22 @@ package nex_datastore_super_mario_maker
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func RateCustomRanking(err error, client *nex.Client, callID uint32, params []*datastore_super_mario_maker.DataStoreRateCustomRankingParam) {
|
||||
|
||||
/*
|
||||
This has to change. We need to figure out what DataStoreRateCustomRankingParam.applicationId means.
|
||||
This method sends a set number of DataStoreRateCustomRankingParam's in the params list depending
|
||||
on what action the user does.
|
||||
|
||||
- Course upload: 4 params
|
||||
- DataStoreRateCustomRankingParam.dataId is always the course ID
|
||||
- DataStoreRateCustomRankingParam.score is always 0
|
||||
- DataStoreRateCustomRankingParam.period is always 36500
|
||||
- DataStoreRateCustomRankingParam.applicationId is:
|
||||
- 0 (seen in other requests relating to Mii data?)
|
||||
- 2400
|
||||
- 200000000
|
||||
- 200002400
|
||||
|
||||
- Course clear: 2 params
|
||||
- DataStoreRateCustomRankingParam.dataId is always the course ID
|
||||
- DataStoreRateCustomRankingParam.score is always 1
|
||||
- DataStoreRateCustomRankingParam.period is always 36500
|
||||
- DataStoreRateCustomRankingParam.applicationId is:
|
||||
- 200000000
|
||||
- 200002400
|
||||
|
||||
- Course star: 12 params
|
||||
- DataStoreRateCustomRankingParam.dataId is:
|
||||
- course ID
|
||||
- course ID
|
||||
- course ID
|
||||
- course ID
|
||||
- course ID
|
||||
- course ID
|
||||
- uploader PID
|
||||
- uploader PID
|
||||
- uploader PID
|
||||
- uploader PID
|
||||
- uploader PID
|
||||
- uploader PID
|
||||
- DataStoreRateCustomRankingParam.score is always 1
|
||||
- DataStoreRateCustomRankingParam.period is:
|
||||
- 36500
|
||||
- 36500
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 36500
|
||||
- 36500
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- 1
|
||||
- DataStoreRateCustomRankingParam.applicationId is:
|
||||
- 0 (seen in other requests relating to Mii data?)
|
||||
- 2400
|
||||
- 119700101
|
||||
- 119702501
|
||||
- 1497730516
|
||||
- 1497732916
|
||||
- 300000000 (seen in other requests relating to Mii data?)
|
||||
- 300002400
|
||||
- 419700101
|
||||
- 419702501
|
||||
- 1797730516
|
||||
- 1797732916
|
||||
|
||||
It seems like many applicationId's are actually a different ID with 2400 added to it?
|
||||
|
||||
Since it always sends a set number of params per action, a quick and dirty way to tell them apart is
|
||||
to check the param list length. However this should NOT be relied on forever and we NEED to learn more
|
||||
about applicationId, as it IS used in many other requests
|
||||
*/
|
||||
|
||||
if len(params) == 2 {
|
||||
// assume "course clear" action
|
||||
database.IncrementCourseClearCount(params[0].DataID)
|
||||
func RateCustomRanking(err error, packet nex.PacketInterface, callID uint32, params []*datastore_super_mario_maker_types.DataStoreRateCustomRankingParam) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
if len(params) == 12 {
|
||||
// assume "star course" action
|
||||
database.IncrementCourseStarCount(params[0].DataID)
|
||||
client := packet.Sender()
|
||||
|
||||
// TODO - Check the period. The real server does check this, just unsure what it means or what the check is
|
||||
for _, param := range params {
|
||||
datastore_smm_db.InsertOrUpdateCustomRanking(param.DataID, param.ApplicationID, param.Score)
|
||||
}
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore_super_mario_maker.ProtocolID, callID)
|
||||
@@ -107,5 +37,7 @@ func RateCustomRanking(err error, client *nex.Client, callID uint32, params []*d
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -2,31 +2,47 @@ package nex_datastore_super_mario_maker
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/utility"
|
||||
)
|
||||
|
||||
func RecommendedCourseSearchObject(err error, client *nex.Client, callID uint32, param *datastore.DataStoreSearchParam, extraData []string) {
|
||||
// TODO: complete this
|
||||
|
||||
pRankingResults := make([]*datastore_super_mario_maker.DataStoreCustomRankingResult, 0)
|
||||
|
||||
// TEMP FOR SHUTTER TO TEST THINGS
|
||||
if client.PID() == 1049991375 {
|
||||
courseMetadata := database.GetCourseMetadataByDataID(145) // specific course shutter wants
|
||||
pRankingResults = append(pRankingResults, utility.CourseMetadataToDataStoreCustomRankingResult(courseMetadata))
|
||||
} else {
|
||||
courseMetadatas := database.GetCourseMetadatasByLimit(100) // In PCAPs param.minimalRatingFrequency is 100 but is 0 here?
|
||||
|
||||
for _, courseMetadata := range courseMetadatas {
|
||||
pRankingResults = append(pRankingResults, utility.CourseMetadataToDataStoreCustomRankingResult(courseMetadata))
|
||||
}
|
||||
func RecommendedCourseSearchObject(err error, packet nex.PacketInterface, callID uint32, param *datastore_types.DataStoreSearchParam, extraData []string) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
client := packet.Sender()
|
||||
|
||||
// * This method is used in 100 Mario and Course World
|
||||
// *
|
||||
// * extraData seems to be a set of filters defining a
|
||||
// * range for a courses success rate
|
||||
// *
|
||||
// * Course World (All) ["", "", "", "0", "0"]
|
||||
// * Course World (Easy) ["1", "0", "34", "0", "0"]
|
||||
// * Course World (Normal) ["1", "35", "74", "0", "0"]
|
||||
// * Course World (Expert) ["1", "75", "95", "0", "0"]
|
||||
// * Course World (Super Expert) ["1", "96", "100", "0", "0"]
|
||||
// *
|
||||
// * Indexes 1 and 2 seem to be a min and max for the *failure*
|
||||
// * rate of the courses. This is not taken into account yet,
|
||||
// * as the SQL query for this would need to be rather complex.
|
||||
// * The last 2 values always seem to be 0, and the first seems
|
||||
// * to always be 1 besides filtering for "All"
|
||||
// *
|
||||
// ! All requests are treated as filtering for "All" right now
|
||||
// TODO - Use these ranges to properly filter by difficulty
|
||||
|
||||
// TODO - Use the offet? Real client never uses it, but might be nice for completeness sake?
|
||||
pRankingResults, errCode := datastore_smm_db.GetRandomCoursesWithLimit(int(param.ResultRange.Length))
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListStructure(pRankingResults)
|
||||
|
||||
@@ -48,5 +64,7 @@ func RecommendedCourseSearchObject(err error, client *nex.Client, callID uint32,
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -4,31 +4,43 @@ import (
|
||||
"strconv"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_types "github.com/PretendoNetwork/nex-protocols-go/datastore/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/utility"
|
||||
)
|
||||
|
||||
func SuggestedCourseSearchObject(err error, client *nex.Client, callID uint32, param *datastore.DataStoreSearchParam, extraData []string) {
|
||||
// TODO: complete this
|
||||
|
||||
courseID, _ := strconv.ParseUint(extraData[0], 0, 64)
|
||||
|
||||
if utility.UserNotOwnCourse(courseID, client.PID()) {
|
||||
database.IncrementCourseAttemptCount(courseID) // We also know this is when a user attempts a course
|
||||
func SuggestedCourseSearchObject(err error, packet nex.PacketInterface, callID uint32, param *datastore_types.DataStoreSearchParam, extraData []string) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
pRankingResults := make([]*datastore_super_mario_maker.DataStoreCustomRankingResult, 0)
|
||||
client := packet.Sender()
|
||||
|
||||
courseMetadatas := database.GetCourseMetadatasByLimit(4) // In PCAPs param.minimalRatingFrequency is 4 but is 0 here?
|
||||
// * This method is called when a course is completed
|
||||
// * to show the scrolling courses at the bottom of the
|
||||
// * screen. extraData[0] is the DataID of the current
|
||||
// * course. extraData[1] and extraData[2] are usually
|
||||
// * 2 and 6 respectively. extraData[3] and extraData[4]
|
||||
// * are always 0? extraData seems to not have any
|
||||
// * effect on the NUMBER of courses returned but likely
|
||||
// * does act as a filter of some kind? Maybe it has to
|
||||
// * do with difficulty? Or ratings?
|
||||
|
||||
for _, courseMetadata := range courseMetadatas {
|
||||
pRankingResults = append(pRankingResults, utility.CourseMetadataToDataStoreCustomRankingResult(courseMetadata))
|
||||
_, err = strconv.ParseUint(extraData[0], 0, 64)
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.InvalidArgument
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.NEXServer)
|
||||
// TODO - Use extraData for filtering
|
||||
pRankingResults, errCode := datastore_smm_db.GetRandomCoursesWithLimit(int(param.ResultRange.Length))
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
rmcResponseStream := nex.NewStreamOut(globals.SecureServer)
|
||||
|
||||
rmcResponseStream.WriteListStructure(pRankingResults)
|
||||
|
||||
@@ -50,5 +62,7 @@ func SuggestedCourseSearchObject(err error, client *nex.Client, callID uint32, p
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,12 +3,23 @@ package nex_datastore_super_mario_maker
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/database"
|
||||
datastore_super_mario_maker_types "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker/types"
|
||||
datastore_smm_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore/super-mario-maker"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func UploadCourseRecord(err error, client *nex.Client, callID uint32, param *datastore_super_mario_maker.DataStoreUploadCourseRecordParam) {
|
||||
database.UpdateCourseWorldRecord(param.DataID, client.PID(), param.Score)
|
||||
func UploadCourseRecord(err error, packet nex.PacketInterface, callID uint32, param *datastore_super_mario_maker_types.DataStoreUploadCourseRecordParam) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
client := packet.Sender()
|
||||
|
||||
errCode := datastore_smm_db.InsertOrUpdateCourseRecord(param.DataID, param.Slot, client.PID(), param.Score)
|
||||
if errCode != 0 {
|
||||
return errCode
|
||||
}
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(datastore_super_mario_maker.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(datastore_super_mario_maker.MethodUploadCourseRecord, nil)
|
||||
@@ -26,5 +37,7 @@ func UploadCourseRecord(err error, client *nex.Client, callID uint32, param *dat
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package nex_message_delivery
|
||||
|
||||
import (
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
@@ -6,7 +6,16 @@ import (
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func DeliverMessage(err error, client *nex.Client, callID uint32, oUserMessage nex.StructureInterface) {
|
||||
func DeliverMessage(err error, packet nex.PacketInterface, callID uint32, oUserMessage *nex.DataHolder) uint32 {
|
||||
if err != nil {
|
||||
globals.Logger.Error(err.Error())
|
||||
return nex.Errors.DataStore.Unknown
|
||||
}
|
||||
|
||||
client := packet.Sender()
|
||||
|
||||
// TODO - See what this does
|
||||
|
||||
rmcResponse := nex.NewRMCResponse(message_delivery.ProtocolID, callID)
|
||||
rmcResponse.SetSuccess(message_delivery.MethodDeliverMessage, nil)
|
||||
|
||||
@@ -23,5 +32,7 @@ func DeliverMessage(err error, client *nex.Client, callID uint32, oUserMessage n
|
||||
responsePacket.AddFlag(nex.FlagNeedsAck)
|
||||
responsePacket.AddFlag(nex.FlagReliable)
|
||||
|
||||
globals.NEXServer.Send(responsePacket)
|
||||
globals.SecureServer.Send(responsePacket)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
30
nex/register_common_authentication_server_protocols.go
Executable file
30
nex/register_common_authentication_server_protocols.go
Executable file
@@ -0,0 +1,30 @@
|
||||
package nex
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
ticket_granting "github.com/PretendoNetwork/nex-protocols-common-go/ticket-granting"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func registerCommonAuthenticationServerProtocols() {
|
||||
ticketGrantingProtocol := ticket_granting.NewCommonTicketGrantingProtocol(globals.AuthenticationServer)
|
||||
|
||||
secureStationURL := nex.NewStationURL("")
|
||||
secureStationURL.SetScheme("prudps")
|
||||
secureStationURL.SetAddress(os.Getenv("PN_SMM_SECURE_SERVER_HOST"))
|
||||
port, _ := strconv.ParseUint(os.Getenv("PN_SMM_SECURE_SERVER_PORT"), 10, 32)
|
||||
secureStationURL.SetPort(uint32(port))
|
||||
secureStationURL.SetCID(1)
|
||||
secureStationURL.SetPID(2)
|
||||
secureStationURL.SetSID(1)
|
||||
secureStationURL.SetStream(10)
|
||||
secureStationURL.SetType(2)
|
||||
|
||||
ticketGrantingProtocol.SetSecureStationURL(secureStationURL)
|
||||
ticketGrantingProtocol.SetBuildName(serverBuildString)
|
||||
|
||||
globals.AuthenticationServer.SetPasswordFromPIDFunction(globals.PasswordFromPID)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package nex
|
||||
|
||||
import (
|
||||
secureconnection "github.com/PretendoNetwork/nex-protocols-common-go/secure-connection"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
nex_secure_connection_common "github.com/PretendoNetwork/super-mario-maker-secure/nex/secure-connection/common"
|
||||
)
|
||||
|
||||
func registerCommonProtocols() {
|
||||
secureConnectionProtocol := secureconnection.NewCommonSecureConnectionProtocol(globals.NEXServer)
|
||||
|
||||
secureConnectionProtocol.AddConnection(nex_secure_connection_common.AddConnection) // * Stubbed
|
||||
secureConnectionProtocol.UpdateConnection(nex_secure_connection_common.UpdateConnection) // * Stubbed
|
||||
secureConnectionProtocol.DoesConnectionExist(nex_secure_connection_common.DoesConnectionExist) // * Stubbed
|
||||
}
|
||||
52
nex/register_common_secure_server_protocols.go
Normal file
52
nex/register_common_secure_server_protocols.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package nex
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
datastore "github.com/PretendoNetwork/nex-protocols-common-go/datastore"
|
||||
secureconnection "github.com/PretendoNetwork/nex-protocols-common-go/secure-connection"
|
||||
datastore_db "github.com/PretendoNetwork/super-mario-maker-secure/database/datastore"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
nex_datastore_super_mario_maker "github.com/PretendoNetwork/super-mario-maker-secure/nex/datastore/super-mario-maker"
|
||||
)
|
||||
|
||||
func registerCommonSecureProtocols() {
|
||||
secureconnection.NewCommonSecureConnectionProtocol(globals.SecureServer)
|
||||
|
||||
commonDataStoreProtocol := datastore.NewCommonDataStoreProtocol(globals.SecureServer)
|
||||
|
||||
commonDataStoreProtocol.SetS3Bucket(os.Getenv("PN_SMM_CONFIG_S3_BUCKET"))
|
||||
commonDataStoreProtocol.SetMinIOClient(globals.MinIOClient)
|
||||
commonDataStoreProtocol.GetObjectInfoByDataID(datastore_db.GetObjectInfoByDataID)
|
||||
commonDataStoreProtocol.UpdateObjectPeriodByDataIDWithPassword(datastore_db.UpdateObjectPeriodByDataIDWithPassword)
|
||||
commonDataStoreProtocol.UpdateObjectMetaBinaryByDataIDWithPassword(datastore_db.UpdateObjectMetaBinaryByDataIDWithPassword)
|
||||
commonDataStoreProtocol.UpdateObjectDataTypeByDataIDWithPassword(datastore_db.UpdateObjectDataTypeByDataIDWithPassword)
|
||||
commonDataStoreProtocol.GetObjectSizeDataID(datastore_db.GetObjectSizeDataID)
|
||||
commonDataStoreProtocol.UpdateObjectUploadCompletedByDataID(datastore_db.UpdateObjectUploadCompletedByDataID)
|
||||
commonDataStoreProtocol.GetObjectInfoByPersistenceTargetWithPassword(datastore_db.GetObjectInfoByPersistenceTargetWithPassword)
|
||||
commonDataStoreProtocol.GetObjectInfoByDataIDWithPassword(datastore_db.GetObjectInfoByDataIDWithPassword)
|
||||
commonDataStoreProtocol.InitializeObjectByPreparePostParam(datastore_db.InitializeObjectByPreparePostParam)
|
||||
commonDataStoreProtocol.InitializeObjectRatingWithSlot(datastore_db.InitializeObjectRatingWithSlot)
|
||||
commonDataStoreProtocol.RateObjectWithPassword(datastore_db.RateObjectWithPassword)
|
||||
commonDataStoreProtocol.DeleteObjectByDataID(datastore_db.DeleteObjectByDataID)
|
||||
commonDataStoreProtocol.GetObjectOwnerByDataID(datastore_db.GetObjectOwnerByDataID)
|
||||
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.GetObjectInfos(nex_datastore_super_mario_maker.GetObjectInfos)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.RateCustomRanking(nex_datastore_super_mario_maker.RateCustomRanking)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.GetCustomRankingByDataID(nex_datastore_super_mario_maker.GetCustomRankingByDataID)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.AddToBufferQueues(nex_datastore_super_mario_maker.AddToBufferQueues)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.GetBufferQueue(nex_datastore_super_mario_maker.GetBufferQueue)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.CompleteAttachFile(nex_datastore_super_mario_maker.CompleteAttachFile)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.PrepareAttachFile(nex_datastore_super_mario_maker.PrepareAttachFile)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.GetApplicationConfig(nex_datastore_super_mario_maker.GetApplicationConfig)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.FollowingsLatestCourseSearchObject(nex_datastore_super_mario_maker.FollowingsLatestCourseSearchObject)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.RecommendedCourseSearchObject(nex_datastore_super_mario_maker.RecommendedCourseSearchObject)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.SuggestedCourseSearchObject(nex_datastore_super_mario_maker.SuggestedCourseSearchObject)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.UploadCourseRecord(nex_datastore_super_mario_maker.UploadCourseRecord)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.GetCourseRecord(nex_datastore_super_mario_maker.GetCourseRecord)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.GetApplicationConfigString(nex_datastore_super_mario_maker.GetApplicationConfigString)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.GetDeletionReason(nex_datastore_super_mario_maker.GetDeletionReason)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.GetMetasWithCourseRecord(nex_datastore_super_mario_maker.GetMetasWithCourseRecord)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.CheckRateCustomRankingCounter(nex_datastore_super_mario_maker.CheckRateCustomRankingCounter)
|
||||
commonDataStoreProtocol.SuperMarioMakerProtocol.CTRPickUpCourseSearchObject(nex_datastore_super_mario_maker.CTRPickUpCourseSearchObject)
|
||||
}
|
||||
@@ -1,42 +1,13 @@
|
||||
package nex
|
||||
|
||||
import (
|
||||
"github.com/PretendoNetwork/nex-protocols-go/datastore"
|
||||
datastore_super_mario_maker "github.com/PretendoNetwork/nex-protocols-go/datastore/super-mario-maker"
|
||||
message_delivery "github.com/PretendoNetwork/nex-protocols-go/message-delivery"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
nex_datastore "github.com/PretendoNetwork/super-mario-maker-secure/nex/datastore"
|
||||
nex_datastore_super_mario_maker "github.com/PretendoNetwork/super-mario-maker-secure/nex/datastore/super-mario-maker"
|
||||
nex_message_delivery "github.com/PretendoNetwork/super-mario-maker-secure/nex/message-delivery"
|
||||
)
|
||||
|
||||
func registerNEXProtocols() {
|
||||
datastoreProtocol := datastore.NewDataStoreProtocol(globals.NEXServer)
|
||||
messageDeliveryProtocol := message_delivery.NewProtocol(globals.SecureServer)
|
||||
|
||||
datastoreProtocol.GetMeta(nex_datastore.GetMeta)
|
||||
datastoreProtocol.PreparePostObject(nex_datastore.PreparePostObject)
|
||||
datastoreProtocol.PrepareGetObject(nex_datastore.PrepareGetObject)
|
||||
datastoreProtocol.CompletePostObject(nex_datastore.CompletePostObject)
|
||||
datastoreProtocol.GetMetasMultipleParam(nex_datastore.GetMetasMultipleParam)
|
||||
datastoreProtocol.ChangeMeta(nex_datastore.ChangeMeta)
|
||||
datastoreProtocol.RateObjects(nex_datastore.RateObjects)
|
||||
|
||||
datastoreSMMProtocol := datastore_super_mario_maker.NewDataStoreSuperMarioMakerProtocol(globals.NEXServer)
|
||||
|
||||
datastoreSMMProtocol.GetObjectInfos(nex_datastore_super_mario_maker.GetObjectInfos)
|
||||
datastoreSMMProtocol.RateCustomRanking(nex_datastore_super_mario_maker.RateCustomRanking)
|
||||
datastoreSMMProtocol.GetCustomRankingByDataId(nex_datastore_super_mario_maker.GetCustomRankingByDataId)
|
||||
datastoreSMMProtocol.AddToBufferQueues(nex_datastore_super_mario_maker.AddToBufferQueues)
|
||||
datastoreSMMProtocol.GetBufferQueue(nex_datastore_super_mario_maker.GetBufferQueue)
|
||||
datastoreSMMProtocol.CompleteAttachFile(nex_datastore_super_mario_maker.CompleteAttachFile)
|
||||
datastoreSMMProtocol.PrepareAttachFile(nex_datastore_super_mario_maker.PrepareAttachFile)
|
||||
datastoreSMMProtocol.GetApplicationConfig(nex_datastore_super_mario_maker.GetApplicationConfig)
|
||||
datastoreSMMProtocol.FollowingsLatestCourseSearchObject(nex_datastore_super_mario_maker.FollowingsLatestCourseSearchObject)
|
||||
datastoreSMMProtocol.RecommendedCourseSearchObject(nex_datastore_super_mario_maker.RecommendedCourseSearchObject)
|
||||
datastoreSMMProtocol.SuggestedCourseSearchObject(nex_datastore_super_mario_maker.SuggestedCourseSearchObject)
|
||||
datastoreSMMProtocol.UploadCourseRecord(nex_datastore_super_mario_maker.UploadCourseRecord)
|
||||
datastoreSMMProtocol.GetCourseRecord(nex_datastore_super_mario_maker.GetCourseRecord)
|
||||
datastoreSMMProtocol.GetApplicationConfigString(nex_datastore_super_mario_maker.GetApplicationConfigString)
|
||||
datastoreSMMProtocol.GetDeletionReason(nex_datastore_super_mario_maker.GetDeletionReason)
|
||||
datastoreSMMProtocol.GetMetasWithCourseRecord(nex_datastore_super_mario_maker.GetMetasWithCourseRecord)
|
||||
datastoreSMMProtocol.CheckRateCustomRankingCounter(nex_datastore_super_mario_maker.CheckRateCustomRankingCounter)
|
||||
datastoreSMMProtocol.CTRPickUpCourseSearchObject(nex_datastore_super_mario_maker.CTRPickUpCourseSearchObject)
|
||||
messageDeliveryProtocol.DeliverMessage(nex_message_delivery.DeliverMessage)
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package nex_secure_connection_common
|
||||
|
||||
func AddConnection(rvcid uint32, urls []string, ip string, port string) {
|
||||
// TODO - Stubbed
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package nex_secure_connection_common
|
||||
|
||||
func DoesConnectionExist(rvcid uint32) bool {
|
||||
// TODO - Stubbed
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package nex_secure_connection_common
|
||||
|
||||
func UpdateConnection(rvcid uint32, urls []string, ip string, port string) {
|
||||
// TODO - Stubbed
|
||||
}
|
||||
33
nex/secure.go
Normal file
33
nex/secure.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package nex
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func StartSecureServer() {
|
||||
globals.SecureServer = nex.NewServer()
|
||||
globals.SecureServer.SetPRUDPVersion(1)
|
||||
globals.SecureServer.SetPRUDPProtocolMinorVersion(2)
|
||||
globals.SecureServer.SetDefaultNEXVersion(nex.NewPatchedNEXVersion(3, 8, 3, "AMAJ"))
|
||||
globals.SecureServer.SetKerberosPassword(globals.KerberosPassword)
|
||||
globals.SecureServer.SetAccessKey("9f2b4678")
|
||||
|
||||
globals.SecureServer.On("Data", func(packet *nex.PacketV1) {
|
||||
request := packet.RMCRequest()
|
||||
|
||||
fmt.Println("==SMM1 - Secure==")
|
||||
fmt.Printf("Protocol ID: %d\n", request.ProtocolID())
|
||||
fmt.Printf("Method ID: %d\n", request.MethodID())
|
||||
fmt.Println("===============")
|
||||
})
|
||||
|
||||
// * Register the common handlers first so that they can be overridden if needed
|
||||
registerCommonSecureProtocols()
|
||||
registerNEXProtocols()
|
||||
|
||||
globals.SecureServer.Listen(fmt.Sprintf(":%s", os.Getenv("PN_SMM_SECURE_SERVER_PORT")))
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package nex
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
nex "github.com/PretendoNetwork/nex-go"
|
||||
"github.com/PretendoNetwork/super-mario-maker-secure/globals"
|
||||
)
|
||||
|
||||
func StartNEXServer() {
|
||||
globals.NEXServer = nex.NewServer()
|
||||
globals.NEXServer.SetPRUDPVersion(1)
|
||||
globals.NEXServer.SetPRUDPProtocolMinorVersion(2)
|
||||
globals.NEXServer.SetDefaultNEXVersion(&nex.NEXVersion{
|
||||
Major: 3,
|
||||
Minor: 8,
|
||||
Patch: 3,
|
||||
})
|
||||
globals.NEXServer.SetKerberosPassword(os.Getenv("KERBEROS_PASSWORD"))
|
||||
globals.NEXServer.SetAccessKey("9f2b4678")
|
||||
|
||||
globals.NEXServer.On("Data", func(packet *nex.PacketV1) {
|
||||
request := packet.RMCRequest()
|
||||
|
||||
fmt.Println("==SMM1 - Secure==")
|
||||
fmt.Printf("Protocol ID: %#v\n", request.ProtocolID())
|
||||
fmt.Printf("Method ID: %#v\n", request.MethodID())
|
||||
fmt.Println("===============")
|
||||
})
|
||||
|
||||
// * Register the common handlers first so that they can be overridden if needed
|
||||
registerCommonProtocols()
|
||||
registerNEXProtocols()
|
||||
|
||||
globals.NEXServer.Listen(":60003")
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user