mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-02-04 17:22:16 +01:00
@@ -18,7 +18,10 @@
|
|||||||
"discord": {
|
"discord": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"bottoken": "",
|
"bottoken": "",
|
||||||
"channelid": ""
|
"realtimeChannelID": "",
|
||||||
|
"serverId": "",
|
||||||
|
"devRoles": [],
|
||||||
|
"devMode": false
|
||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ type SaveDumpOptions struct {
|
|||||||
type Discord struct {
|
type Discord struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
BotToken string
|
BotToken string
|
||||||
ChannelID string
|
ServerID string
|
||||||
|
RealtimeChannelID string
|
||||||
|
DevRoles []string
|
||||||
|
DevMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Database holds the postgres database config.
|
// Database holds the postgres database config.
|
||||||
@@ -154,4 +157,3 @@ func LoadConfig() (*Config, error) {
|
|||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
26
Erupe/distitem.sql
Normal file
26
Erupe/distitem.sql
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
BEGIN;
|
||||||
|
CREATE TABLE public.distribution
|
||||||
|
(
|
||||||
|
id serial NOT NULL PRIMARY KEY,
|
||||||
|
character_id int,
|
||||||
|
type int NOT NULL,
|
||||||
|
deadline timestamp without time zone,
|
||||||
|
event_name text NOT NULL DEFAULT 'GM Gift!',
|
||||||
|
description text NOT NULL DEFAULT '~C05You received a gift!',
|
||||||
|
times_acceptable int NOT NULL DEFAULT 1,
|
||||||
|
min_hr int NOT NULL DEFAULT 65535,
|
||||||
|
max_hr int NOT NULL DEFAULT 65535,
|
||||||
|
min_sr int NOT NULL DEFAULT 65535,
|
||||||
|
max_sr int NOT NULL DEFAULT 65535,
|
||||||
|
min_gr int NOT NULL DEFAULT 65535,
|
||||||
|
max_gr int NOT NULL DEFAULT 65535,
|
||||||
|
data bytea NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE public.distributions_accepted
|
||||||
|
(
|
||||||
|
distribution_id int,
|
||||||
|
character_id int
|
||||||
|
);
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Solenataris/Erupe/config"
|
"github.com/Solenataris/Erupe/config"
|
||||||
"github.com/Solenataris/Erupe/server/channelserver"
|
"github.com/Solenataris/Erupe/server/channelserver"
|
||||||
|
"github.com/Solenataris/Erupe/server/discordbot"
|
||||||
"github.com/Solenataris/Erupe/server/entranceserver"
|
"github.com/Solenataris/Erupe/server/entranceserver"
|
||||||
"github.com/Solenataris/Erupe/server/launcherserver"
|
"github.com/Solenataris/Erupe/server/launcherserver"
|
||||||
"github.com/Solenataris/Erupe/server/signserver"
|
"github.com/Solenataris/Erupe/server/signserver"
|
||||||
@@ -39,6 +40,31 @@ func main() {
|
|||||||
logger.Fatal("Failed to load config", zap.Error(err))
|
logger.Fatal("Failed to load config", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discord bot
|
||||||
|
var discordBot *discordbot.DiscordBot = nil
|
||||||
|
|
||||||
|
if erupeConfig.Discord.Enabled {
|
||||||
|
bot, err := discordbot.NewDiscordBot(discordbot.DiscordBotOptions{
|
||||||
|
Logger: logger,
|
||||||
|
Config: erupeConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("Failed to create discord bot", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discord bot
|
||||||
|
err = bot.Start()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("Failed to starts discord bot", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
discordBot = bot
|
||||||
|
} else {
|
||||||
|
logger.Info("Discord bot is disabled")
|
||||||
|
}
|
||||||
|
|
||||||
// Create the postgres DB pool.
|
// Create the postgres DB pool.
|
||||||
connectString := fmt.Sprintf(
|
connectString := fmt.Sprintf(
|
||||||
"host=%s port=%d user=%s password=%s dbname= %s sslmode=disable",
|
"host=%s port=%d user=%s password=%s dbname= %s sslmode=disable",
|
||||||
@@ -116,6 +142,9 @@ func main() {
|
|||||||
Logger: logger.Named("channel"),
|
Logger: logger.Named("channel"),
|
||||||
ErupeConfig: erupeConfig,
|
ErupeConfig: erupeConfig,
|
||||||
DB: db,
|
DB: db,
|
||||||
|
Name: erupeConfig.Entrance.Entries[0].Name,
|
||||||
|
Enable: erupeConfig.Entrance.Entries[0].Channels[0].MaxPlayers > 0,
|
||||||
|
//DiscordBot: discordBot,
|
||||||
})
|
})
|
||||||
|
|
||||||
err = channelServer1.Start(erupeConfig.Channel.Port1)
|
err = channelServer1.Start(erupeConfig.Channel.Port1)
|
||||||
@@ -129,6 +158,9 @@ func main() {
|
|||||||
Logger: logger.Named("channel"),
|
Logger: logger.Named("channel"),
|
||||||
ErupeConfig: erupeConfig,
|
ErupeConfig: erupeConfig,
|
||||||
DB: db,
|
DB: db,
|
||||||
|
Name: erupeConfig.Entrance.Entries[1].Name,
|
||||||
|
Enable: erupeConfig.Entrance.Entries[1].Channels[0].MaxPlayers > 0,
|
||||||
|
DiscordBot: discordBot,
|
||||||
})
|
})
|
||||||
|
|
||||||
err = channelServer2.Start(erupeConfig.Channel.Port2)
|
err = channelServer2.Start(erupeConfig.Channel.Port2)
|
||||||
@@ -141,6 +173,9 @@ func main() {
|
|||||||
Logger: logger.Named("channel"),
|
Logger: logger.Named("channel"),
|
||||||
ErupeConfig: erupeConfig,
|
ErupeConfig: erupeConfig,
|
||||||
DB: db,
|
DB: db,
|
||||||
|
Name: erupeConfig.Entrance.Entries[2].Name,
|
||||||
|
Enable: erupeConfig.Entrance.Entries[2].Channels[0].MaxPlayers > 0,
|
||||||
|
//DiscordBot: discordBot,
|
||||||
})
|
})
|
||||||
|
|
||||||
err = channelServer3.Start(erupeConfig.Channel.Port3)
|
err = channelServer3.Start(erupeConfig.Channel.Port3)
|
||||||
@@ -153,6 +188,9 @@ func main() {
|
|||||||
Logger: logger.Named("channel"),
|
Logger: logger.Named("channel"),
|
||||||
ErupeConfig: erupeConfig,
|
ErupeConfig: erupeConfig,
|
||||||
DB: db,
|
DB: db,
|
||||||
|
Name: erupeConfig.Entrance.Entries[3].Name,
|
||||||
|
Enable: erupeConfig.Entrance.Entries[3].Channels[0].MaxPlayers > 0,
|
||||||
|
//DiscordBot: discordBot,
|
||||||
})
|
})
|
||||||
|
|
||||||
err = channelServer4.Start(erupeConfig.Channel.Port4)
|
err = channelServer4.Start(erupeConfig.Channel.Port4)
|
||||||
|
|||||||
39
Erupe/road-shop-rotation.sql
Normal file
39
Erupe/road-shop-rotation.sql
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
BEGIN;
|
||||||
|
CREATE TABLE IF NOT EXISTS public.normal_shop_items
|
||||||
|
(
|
||||||
|
shoptype integer,
|
||||||
|
shopid integer,
|
||||||
|
itemhash integer not null,
|
||||||
|
itemid integer,
|
||||||
|
points integer,
|
||||||
|
tradequantity integer,
|
||||||
|
rankreqlow integer,
|
||||||
|
rankreqhigh integer,
|
||||||
|
rankreqg integer,
|
||||||
|
storelevelreq integer,
|
||||||
|
maximumquantity integer,
|
||||||
|
boughtquantity integer,
|
||||||
|
roadfloorsrequired integer,
|
||||||
|
weeklyfataliskills integer,
|
||||||
|
enable_weeks character varying(8)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.normal_shop_items
|
||||||
|
(
|
||||||
|
ADD COLUMN enable_weeks character varying(8)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.shop_item_state
|
||||||
|
(
|
||||||
|
char_id bigint REFERENCES characters (id),
|
||||||
|
itemhash int UNIQUE NOT NULL,
|
||||||
|
usedquantity int,
|
||||||
|
week int
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.shop_item_state
|
||||||
|
(
|
||||||
|
ADD COLUMN week int
|
||||||
|
);
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -143,9 +143,8 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
fmt.Printf("Got chat message: %+v\n", chatMessage)
|
fmt.Printf("Got chat message: %+v\n", chatMessage)
|
||||||
|
|
||||||
// Discord integration
|
// Discord integration
|
||||||
if s.server.erupeConfig.Discord.Enabled {
|
if chatMessage.Type == binpacket.ChatTypeLocal || chatMessage.Type == binpacket.ChatTypeParty {
|
||||||
message := fmt.Sprintf("%s: %s", chatMessage.SenderName, chatMessage.Message)
|
s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
|
||||||
s.server.discordSession.ChannelMessageSend(s.server.erupeConfig.Discord.ChannelID, message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RAVI COMMANDS V2
|
// RAVI COMMANDS V2
|
||||||
|
|||||||
@@ -2,162 +2,344 @@ package channelserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Character struct {
|
||||||
|
ID uint32 `db:"id"`
|
||||||
|
IsFemale bool `db:"is_female"`
|
||||||
|
IsNewCharacter bool `db:"is_new_character"`
|
||||||
|
Name string `db:"name"`
|
||||||
|
UnkDescString string `db:"unk_desc_string"`
|
||||||
|
HRP uint16 `db:"hrp"`
|
||||||
|
GR uint16 `db:"gr"`
|
||||||
|
WeaponType uint16 `db:"weapon_type"`
|
||||||
|
LastLogin uint32 `db:"last_login"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var weapons = []string{
|
||||||
|
"<:gs:970861408227049477>",
|
||||||
|
"<:hbg:970861408281563206>",
|
||||||
|
"<:hm:970861408239628308>",
|
||||||
|
"<:lc:970861408298352660>",
|
||||||
|
"<:sns:970861408319315988>",
|
||||||
|
"<:lbg:970861408327725137>",
|
||||||
|
"<:ds:970861408277368883>",
|
||||||
|
"<:ls:970861408319311872>",
|
||||||
|
"<:hh:970861408222863400>",
|
||||||
|
"<:gl:970861408327720980>",
|
||||||
|
"<:bw:970861408294174780>",
|
||||||
|
"<:tf:970861408424177744>",
|
||||||
|
"<:sw:970861408340283472>",
|
||||||
|
"<:ms:970861408411594842>",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) getCharacterForUser(uid int) (*Character, error) {
|
||||||
|
character := Character{}
|
||||||
|
err := s.db.Get(&character, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE id = $1", uid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &character, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountChars(s *Server) string {
|
||||||
|
count := 0
|
||||||
|
for _, stage := range s.stages {
|
||||||
|
count += len(stage.clients)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf("Server [%s]: %d players;", s.name, count)
|
||||||
|
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListPlayer struct {
|
||||||
|
CharName string
|
||||||
|
InQuest bool
|
||||||
|
WeaponEmoji string
|
||||||
|
QuestEmoji string
|
||||||
|
StageName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ListPlayer) toString(length int) string {
|
||||||
|
status := ""
|
||||||
|
if p.InQuest {
|
||||||
|
status = "(in quest " + p.QuestEmoji + ")"
|
||||||
|
} else {
|
||||||
|
status = p.StageName
|
||||||
|
}
|
||||||
|
|
||||||
|
missingSpace := length - len(p.CharName)
|
||||||
|
whitespaces := strings.Repeat(" ", missingSpace+5)
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s %s %s %s", p.WeaponEmoji, p.CharName, whitespaces, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPlayerList(s *Server) ([]ListPlayer, int) {
|
||||||
|
list := []ListPlayer{}
|
||||||
|
questEmojis := []string{
|
||||||
|
":white_circle:",
|
||||||
|
":red_circle:",
|
||||||
|
":blue_circle:",
|
||||||
|
":brown_circle:",
|
||||||
|
":green_circle:",
|
||||||
|
":purple_circle:",
|
||||||
|
":yellow_circle:",
|
||||||
|
":orange_circle:",
|
||||||
|
}
|
||||||
|
|
||||||
|
bigNameLen := 0
|
||||||
|
|
||||||
|
for _, stage := range s.stages {
|
||||||
|
if len(stage.clients) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
questEmoji := ":black_circle:"
|
||||||
|
|
||||||
|
if len(questEmojis) > 0 {
|
||||||
|
questEmoji = questEmojis[len(questEmojis)-1]
|
||||||
|
questEmojis = questEmojis[:len(questEmojis)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
isQuest := stage.isQuest()
|
||||||
|
for client := range stage.clients {
|
||||||
|
char, err := s.getCharacterForUser(int(client.charID))
|
||||||
|
if err == nil {
|
||||||
|
if len(char.Name) > bigNameLen {
|
||||||
|
bigNameLen = len(char.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
list = append(list, ListPlayer{
|
||||||
|
CharName: char.Name,
|
||||||
|
InQuest: isQuest,
|
||||||
|
QuestEmoji: questEmoji,
|
||||||
|
WeaponEmoji: weapons[char.WeaponType],
|
||||||
|
StageName: stage.GetName(),
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, bigNameLen
|
||||||
|
}
|
||||||
|
|
||||||
|
func PlayerList(s *Server) string {
|
||||||
|
list := ""
|
||||||
|
count := 0
|
||||||
|
listPlayers, bigNameLen := getPlayerList(s)
|
||||||
|
|
||||||
|
sort.SliceStable(listPlayers, func(i, j int) bool {
|
||||||
|
return listPlayers[i].CharName < listPlayers[j].CharName
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, lp := range listPlayers {
|
||||||
|
list += lp.toString(bigNameLen) + "\n"
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf("<:SnS:822963937360347148> Frontier Hunters Online: [%s ] <:switcha:822963906401533992> \n============== Total %d =============\n", s.name, count)
|
||||||
|
message += list
|
||||||
|
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func debug(s *Server) string {
|
||||||
|
list := ""
|
||||||
|
|
||||||
|
for _, stage := range s.stages {
|
||||||
|
if !stage.isQuest() && len(stage.objects) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
list += fmt.Sprintf(" -> Stage: %s StageId: %s\n", stage.GetName(), stage.id)
|
||||||
|
isQuest := "false"
|
||||||
|
hasDeparted := "false"
|
||||||
|
|
||||||
|
if stage.isQuest() {
|
||||||
|
isQuest = "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
list += fmt.Sprintf(" '-> isQuest: %s\n", isQuest)
|
||||||
|
|
||||||
|
if stage.isQuest() {
|
||||||
|
if stage.hasDeparted {
|
||||||
|
hasDeparted = "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
list += fmt.Sprintf(" '-> isDeparted: %s\n", hasDeparted)
|
||||||
|
list += fmt.Sprintf(" '-> reserveSlots (%d/%d)\n", len(stage.reservedClientSlots), stage.maxPlayers)
|
||||||
|
|
||||||
|
for charid, _ := range stage.reservedClientSlots {
|
||||||
|
char, err := s.getCharacterForUser(int(charid))
|
||||||
|
if err == nil {
|
||||||
|
list += fmt.Sprintf(" '-> %s\n", char.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list += " '-> objects: \n"
|
||||||
|
for _, obj := range stage.objects {
|
||||||
|
objInfo := fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z)
|
||||||
|
list += fmt.Sprintf(" '-> ObjectId: %d - %s\n", obj.id, objInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf("Objects in Server: [%s ]\n", s.name)
|
||||||
|
message += list
|
||||||
|
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func questlist(s *Server) string {
|
||||||
|
list := ""
|
||||||
|
|
||||||
|
for _, stage := range s.stages {
|
||||||
|
if !stage.isQuest() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hasDeparted := ""
|
||||||
|
if stage.hasDeparted {
|
||||||
|
hasDeparted = " - departed"
|
||||||
|
}
|
||||||
|
list += fmt.Sprintf(" '-> StageId: %s (%d/%d) %s - %s\n", stage.id, len(stage.reservedClientSlots), stage.maxPlayers, hasDeparted, stage.createdAt)
|
||||||
|
|
||||||
|
for charid, _ := range stage.reservedClientSlots {
|
||||||
|
char, err := s.getCharacterForUser(int(charid))
|
||||||
|
if err == nil {
|
||||||
|
list += fmt.Sprintf(" '-> %s\n", char.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf("Quests in Server: [%s ]\n", s.name)
|
||||||
|
message += list
|
||||||
|
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeStageById(s *Server, stageId string) string {
|
||||||
|
if s.stages[stageId] != nil {
|
||||||
|
delete(s.stages, stageId)
|
||||||
|
return "Stage deleted!"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Stage not found!"
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanStr(str string) string {
|
||||||
|
return strings.ToLower(strings.Trim(str, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCharInfo(server *Server, charName string) string {
|
||||||
|
var s *Stage
|
||||||
|
var c *Session
|
||||||
|
|
||||||
|
for _, stage := range server.stages {
|
||||||
|
for client := range stage.clients {
|
||||||
|
|
||||||
|
if client.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if cleanStr(client.Name) == cleanStr(charName) {
|
||||||
|
s = stage
|
||||||
|
c = client
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return "Character not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
objInfo := ""
|
||||||
|
|
||||||
|
obj := server.FindStageObjectByChar(c.charID)
|
||||||
|
// server.logger.Info("Found object: %+v", zap.Object("obj", obj))
|
||||||
|
|
||||||
|
if obj != nil {
|
||||||
|
objInfo = fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Character: %s\nStage: %s\nStageId: %s\n%s", c.Name, s.GetName(), s.id, objInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) isDiscordAdmin(ds *discordgo.Session, m *discordgo.MessageCreate) bool {
|
||||||
|
for _, role := range m.Member.Roles {
|
||||||
|
for _, id := range s.erupeConfig.Discord.DevRoles {
|
||||||
|
if id == role {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// onDiscordMessage handles receiving messages from discord and forwarding them ingame.
|
// onDiscordMessage handles receiving messages from discord and forwarding them ingame.
|
||||||
func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) {
|
func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
// Ignore messages from our bot, or ones that are not in the correct channel.
|
// Ignore messages from our bot, or ones that are not in the correct channel.
|
||||||
if m.Author.ID == ds.State.User.ID || m.ChannelID != s.erupeConfig.Discord.ChannelID {
|
if m.Author.ID == ds.State.User.ID || !s.enable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast to the game clients.
|
// Ignore other channels in devMode
|
||||||
data := m.Content
|
if s.erupeConfig.Discord.DevMode && m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID {
|
||||||
|
|
||||||
// Split on comma.
|
|
||||||
result := strings.Split(data, " ")
|
|
||||||
|
|
||||||
if result[0] == "!maintenancedate" && m.Author.ID == "836027554628370492" {
|
|
||||||
replaceDays := dayConvert(result[1])
|
|
||||||
replaceMonth := MonthConvert(result[3])
|
|
||||||
|
|
||||||
s.BroadcastChatMessage("MAINTENANCE EXCEPTIONNELLE :")
|
|
||||||
s.BroadcastChatMessage("Les serveurs seront temporairement inaccessibles le")
|
|
||||||
s.BroadcastChatMessage(fmt.Sprintf("%s %s %s %s a partir de %s heures et %s minutes.", replaceDays, result[2], replaceMonth, result[4], result[5], result[6])) // Jour Mois Année Heures Minutes
|
|
||||||
s.BroadcastChatMessage("Evitez de vous connecter durant cette periode. Veuillez nous")
|
|
||||||
s.BroadcastChatMessage("excuser pour la gene occasionee. Merci de votre cooperation.")
|
|
||||||
return
|
|
||||||
} else if result[0] == "!maintenance" && m.Author.ID == "836027554628370492" {
|
|
||||||
s.BroadcastChatMessage("RAPPEL DE MAINTENANCE DU MARDI (18H-22H): Les serveurs seront")
|
|
||||||
s.BroadcastChatMessage("temporairement inaccessibles dans 15 minutes. Veuillez ne pas")
|
|
||||||
s.BroadcastChatMessage("vous connecter ou deconnectez-vous maintenant, afin de ne pas")
|
|
||||||
s.BroadcastChatMessage("perturber les operations de maintenance. Veuillez nous")
|
|
||||||
s.BroadcastChatMessage("excuser pour la gene occasionnee. Merci de votre cooperation.")
|
|
||||||
s.TimerUpdate(15, 0, true)
|
|
||||||
return
|
|
||||||
} else if result[0] == "!Rmaintenancedate" && m.Author.ID == "836027554628370492" {
|
|
||||||
s.BroadcastChatMessage("RAPPEL DE MAINTENANCE EXCEPTIONNELLE: Les serveurs seront")
|
|
||||||
s.BroadcastChatMessage("temporairement inaccessibles dans 15 minutes. Veuillez ne pas")
|
|
||||||
s.BroadcastChatMessage("vous connecter ou deconnectez-vous maintenant, afin de ne pas")
|
|
||||||
s.BroadcastChatMessage("perturber les operations de maintenance. Veuillez nous")
|
|
||||||
s.BroadcastChatMessage("excuser pour la gene occasionnee. Merci de votre cooperation.")
|
|
||||||
s.TimerUpdate(15, 1, true)
|
|
||||||
return
|
|
||||||
} else if result[0] == "!maintenanceStop" && m.Author.ID == "836027554628370492" {
|
|
||||||
s.BroadcastChatMessage("INFORMATION: A titre exceptionnel, il n'y aura pas de")
|
|
||||||
s.BroadcastChatMessage("maintenance de 18h a 22h, vous pouvez continuer de jouer")
|
|
||||||
s.BroadcastChatMessage("librement jusqu'a la prochaine annonce de maintenance !")
|
|
||||||
s.BroadcastChatMessage("Bonne chasse !")
|
|
||||||
s.TimerUpdate(0, 0, false)
|
|
||||||
return
|
|
||||||
} else if result[0] == "!maintenanceStopExec" && m.Author.ID == "836027554628370492" {
|
|
||||||
replaceDays := dayConvert(result[1])
|
|
||||||
replaceMonth := MonthConvert(result[3])
|
|
||||||
|
|
||||||
s.BroadcastChatMessage("INFORMATION: A titre exceptionnel, il n'y aura pas de")
|
|
||||||
s.BroadcastChatMessage(fmt.Sprintf("maintenance le %s %s %s %s a partir de", replaceDays, result[2], replaceMonth, result[4])) // Jour Mois Année
|
|
||||||
s.BroadcastChatMessage(fmt.Sprintf("%s heures et %s minutes, vous pouvez continuer de jouer", result[5], result[6])) // Heures Minutes
|
|
||||||
s.BroadcastChatMessage("librement jusqu'a la prochaine annonce de maintenance !")
|
|
||||||
s.BroadcastChatMessage("Bonne chasse !")
|
|
||||||
s.TimerUpdate(0, 1, false)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args := strings.Split(m.Content, " ")
|
||||||
|
commandName := args[0]
|
||||||
|
|
||||||
|
// Move to slash commadns
|
||||||
|
if commandName == "!players" {
|
||||||
|
ds.ChannelMessageSend(m.ChannelID, PlayerList(s))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandName == "-char" {
|
||||||
|
if len(args) < 2 {
|
||||||
|
ds.ChannelMessageSend(m.ChannelID, "Usage: !char <char name>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
charName := strings.Join(args[1:], " ")
|
||||||
|
ds.ChannelMessageSend(m.ChannelID, getCharInfo(s, charName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandName == "!debug" && s.isDiscordAdmin(ds, m) {
|
||||||
|
ds.ChannelMessageSend(m.ChannelID, debug(s))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandName == "!questlist" && s.isDiscordAdmin(ds, m) {
|
||||||
|
ds.ChannelMessageSend(m.ChannelID, questlist(s))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandName == "!remove-stage" && s.isDiscordAdmin(ds, m) {
|
||||||
|
if len(args) < 2 {
|
||||||
|
ds.ChannelMessageSend(m.ChannelID, "Usage: !remove-stage <stage id>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stageId := strings.Join(args[1:], " ")
|
||||||
|
ds.ChannelMessageSend(m.ChannelID, removeStageById(s, stageId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.ChannelID == s.erupeConfig.Discord.RealtimeChannelID {
|
||||||
message := fmt.Sprintf("[DISCORD] %s: %s", m.Author.Username, m.Content)
|
message := fmt.Sprintf("[DISCORD] %s: %s", m.Author.Username, m.Content)
|
||||||
s.BroadcastChatMessage(message)
|
s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message))
|
||||||
}
|
|
||||||
|
|
||||||
func dayConvert(result string) string {
|
|
||||||
var replaceDays string
|
|
||||||
|
|
||||||
if result == "1" {
|
|
||||||
replaceDays = "Lundi"
|
|
||||||
} else if result == "2" {
|
|
||||||
replaceDays = "Mardi"
|
|
||||||
} else if result == "3" {
|
|
||||||
replaceDays = "Mercredi"
|
|
||||||
} else if result == "4" {
|
|
||||||
replaceDays = "Jeudi"
|
|
||||||
} else if result == "5" {
|
|
||||||
replaceDays = "Vendredi"
|
|
||||||
} else if result == "6" {
|
|
||||||
replaceDays = "Samedi"
|
|
||||||
} else if result == "7" {
|
|
||||||
replaceDays = "Dimanche"
|
|
||||||
} else {
|
|
||||||
replaceDays = "NULL"
|
|
||||||
}
|
|
||||||
|
|
||||||
return replaceDays
|
|
||||||
}
|
|
||||||
|
|
||||||
func MonthConvert(result string) string {
|
|
||||||
var replaceMonth string
|
|
||||||
|
|
||||||
if result == "01" {
|
|
||||||
replaceMonth = "Janvier"
|
|
||||||
} else if result == "02" {
|
|
||||||
replaceMonth = "Fevrier"
|
|
||||||
} else if result == "03" {
|
|
||||||
replaceMonth = "Mars"
|
|
||||||
} else if result == "04" {
|
|
||||||
replaceMonth = "Avril"
|
|
||||||
} else if result == "05" {
|
|
||||||
replaceMonth = "Mai"
|
|
||||||
} else if result == "06" {
|
|
||||||
replaceMonth = "Juin"
|
|
||||||
} else if result == "07" {
|
|
||||||
replaceMonth = "Juillet"
|
|
||||||
} else if result == "08" {
|
|
||||||
replaceMonth = "Aout"
|
|
||||||
} else if result == "09" {
|
|
||||||
replaceMonth = "Septembre"
|
|
||||||
} else if result == "10" {
|
|
||||||
replaceMonth = "Octobre"
|
|
||||||
} else if result == "11" {
|
|
||||||
replaceMonth = "Novembre"
|
|
||||||
} else if result == "12" {
|
|
||||||
replaceMonth = "Decembre"
|
|
||||||
} else {
|
|
||||||
replaceMonth = "NULL"
|
|
||||||
}
|
|
||||||
|
|
||||||
return replaceMonth
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) TimerUpdate(timer int, typeStop int, disableAutoOff bool) {
|
|
||||||
timertotal := 0
|
|
||||||
for timer > 0 {
|
|
||||||
time.Sleep(1 * time.Minute)
|
|
||||||
timer -= 1
|
|
||||||
timertotal += 1
|
|
||||||
if disableAutoOff {
|
|
||||||
// Un message s'affiche toutes les 10 minutes pour prévenir de la maintenance.
|
|
||||||
if timertotal == 10 {
|
|
||||||
timertotal = 0
|
|
||||||
if typeStop == 0 {
|
|
||||||
s.BroadcastChatMessage("RAPPEL DE MAINTENANCE DU MARDI (18H-22H): Les serveurs seront")
|
|
||||||
s.BroadcastChatMessage(fmt.Sprintf("temporairement inaccessibles dans %d minutes. Veuillez ne pas", timer))
|
|
||||||
s.BroadcastChatMessage("vous connecter ou deconnectez-vous maintenant, afin de ne pas")
|
|
||||||
s.BroadcastChatMessage("perturber les operations de maintenance. Veuillez nous excuser")
|
|
||||||
s.BroadcastChatMessage("pour la gene occasionnee. Merci de votre cooperation.")
|
|
||||||
} else {
|
|
||||||
s.BroadcastChatMessage("RAPPEL DE MAINTENANCE EXCEPTIONNELLE: Les serveurs seront")
|
|
||||||
s.BroadcastChatMessage(fmt.Sprintf("temporairement inaccessibles dans %d minutes. Veuillez ne pas", timer))
|
|
||||||
s.BroadcastChatMessage("vous connecter ou deconnectez-vous maintenant, afin de ne pas")
|
|
||||||
s.BroadcastChatMessage("perturber les operations de maintenance. Veuillez nous excuser")
|
|
||||||
s.BroadcastChatMessage("pour la gene occasionnee. Merci de votre cooperation.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Déconnecter tous les joueurs du serveur.
|
|
||||||
if timer == 0 {
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ type ItemDist struct {
|
|||||||
func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateDistItem)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateDistItem)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
distCount := 0
|
distCount := 0
|
||||||
dists, err := s.server.db.Queryx(`
|
dists, err := s.server.db.Queryx(`
|
||||||
SELECT d.id, event_name, description, times_acceptable,
|
SELECT d.id, event_name, description, times_acceptable,
|
||||||
@@ -55,7 +54,6 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Error parsing item distribution data", zap.Error(err))
|
s.logger.Error("Error parsing item distribution data", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint32(distData.ID)
|
bf.WriteUint32(distData.ID)
|
||||||
bf.WriteUint32(distData.Deadline)
|
bf.WriteUint32(distData.Deadline)
|
||||||
bf.WriteUint32(0) // Unk
|
bf.WriteUint32(0) // Unk
|
||||||
@@ -71,8 +69,8 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint32(0) // Unk
|
bf.WriteUint32(0) // Unk
|
||||||
bf.WriteUint32(0) // Unk
|
bf.WriteUint32(0) // Unk
|
||||||
eventName, _ := stringsupport.ConvertUTF8ToShiftJIS(distData.EventName)
|
eventName, _ := stringsupport.ConvertUTF8ToShiftJIS(distData.EventName)
|
||||||
bf.WriteUint16(uint16(len(eventName)))
|
bf.WriteUint16(uint16(len(eventName)+1))
|
||||||
bf.WriteBytes(eventName)
|
bf.WriteNullTerminatedBytes(eventName)
|
||||||
bf.WriteBytes(make([]byte, 391))
|
bf.WriteBytes(make([]byte, 391))
|
||||||
}
|
}
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package channelserver
|
package channelserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
@@ -8,10 +9,13 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
|
||||||
"strings"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Andoryuuta/byteframe"
|
"github.com/Andoryuuta/byteframe"
|
||||||
"github.com/Solenataris/Erupe/common/bfutil"
|
"github.com/Solenataris/Erupe/common/bfutil"
|
||||||
@@ -19,6 +23,8 @@ import (
|
|||||||
"github.com/Solenataris/Erupe/network/mhfpacket"
|
"github.com/Solenataris/Erupe/network/mhfpacket"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/text/encoding/japanese"
|
||||||
|
"golang.org/x/text/transform"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FestivalColour string
|
type FestivalColour string
|
||||||
@@ -1515,7 +1521,13 @@ func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
// uint32 expiration timestamp
|
// uint32 expiration timestamp
|
||||||
|
|
||||||
// encourage food
|
// encourage food
|
||||||
data := []byte{0x00, 0x01, 0x0F, 0x51, 0x97, 0xFF, 0x00, 0x00, 0x02, 0xC4, 0x00, 0x00, 0x00, 0x03, 0x5F, 0xFC, 0x0B, 0x51}
|
data := []byte{0x00, 0x06,
|
||||||
|
0x0F, 0x51, 0x97, 0xFF, 0x00, 0x00, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFC, 0x0B, 0x51,
|
||||||
|
0x0F, 0x51, 0x97, 0xFF, 0x00, 0x00, 0x02, 0x9c, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFC, 0x0B, 0x52,
|
||||||
|
0x0F, 0x51, 0x97, 0xFF, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFC, 0x0B, 0x51,
|
||||||
|
0x0F, 0x51, 0x97, 0xFF, 0x00, 0x00, 0x01, 0x8b, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFD, 0x0B, 0x51,
|
||||||
|
0x0F, 0x51, 0x97, 0xFF, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFC, 0x0B, 0x51,
|
||||||
|
0x0F, 0x51, 0x97, 0xFF, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x03, 0xF3, 0xFC, 0x0B, 0x51}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
//data := []byte{0x00, 0x01, 0x1C, 0x72, 0x54, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5F, 0xF8, 0x2F, 0xE1}
|
//data := []byte{0x00, 0x01, 0x1C, 0x72, 0x54, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5F, 0xF8, 0x2F, 0xE1}
|
||||||
//doAckBufSucceed(s, pkt.AckHandle, data)
|
//doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
@@ -1566,7 +1578,7 @@ func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
msgs, err := s.server.db.Queryx("SELECT post_type, stamp_id, title, body, author_id, (EXTRACT(epoch FROM created_at)::int) as created_at, liked_by FROM guild_posts WHERE guild_id = $1 AND post_type = $2 ORDER BY created_at DESC", guild.ID, int(pkt.BoardType))
|
msgs, err := s.server.db.Queryx("SELECT post_type, stamp_id, title, body, author_id, (EXTRACT(epoch FROM created_at)::int) as created_at, liked_by FROM guild_posts WHERE guild_id = $1 AND post_type = $2 ORDER BY created_at DESC", guild.ID, int(pkt.BoardType))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to get guild messages from db", zap.Error(err))
|
log.Println("Failed to get guild messages from db", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
@@ -1575,22 +1587,38 @@ func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
for msgs.Next() {
|
for msgs.Next() {
|
||||||
noMsgs = false
|
noMsgs = false
|
||||||
postCount++
|
postCount++
|
||||||
|
var str_title, str_body string
|
||||||
|
var timestampst, timecomp uint64
|
||||||
|
timestampst = 32400
|
||||||
|
|
||||||
postData := &MessageBoardPost{}
|
postData := &MessageBoardPost{}
|
||||||
err = msgs.StructScan(&postData)
|
err = msgs.StructScan(&postData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to read guild message board post")
|
log.Println("Failed to get guild messages from db", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t := japanese.ShiftJIS.NewEncoder()
|
||||||
|
str_title, _, err := transform.String(t, postData.Title)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
str_body, _, err = transform.String(t, postData.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
timecomp = postData.Timestamp - timestampst
|
||||||
|
|
||||||
bf.WriteUint32(postData.Type)
|
bf.WriteUint32(postData.Type)
|
||||||
bf.WriteUint32(postData.AuthorID)
|
bf.WriteUint32(postData.AuthorID)
|
||||||
bf.WriteUint64(postData.Timestamp)
|
bf.WriteUint64(timecomp)
|
||||||
liked := false
|
liked := false
|
||||||
likedBySlice := strings.Split(postData.LikedBy, ",")
|
likedBySlice := strings.Split(postData.LikedBy, ",")
|
||||||
for i := 0; i < len(likedBySlice); i++ {
|
for i := 0; i < len(likedBySlice); i++ {
|
||||||
j, _ := strconv.ParseInt(likedBySlice[i], 10, 64)
|
j, _ := strconv.ParseInt(likedBySlice[i], 10, 64)
|
||||||
if int(j) == int(s.charID) {
|
if int(j) == int(s.charID) {
|
||||||
liked = true; break
|
liked = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if likedBySlice[0] == "" {
|
if likedBySlice[0] == "" {
|
||||||
@@ -1598,12 +1626,13 @@ func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
} else {
|
} else {
|
||||||
bf.WriteUint32(uint32(len(likedBySlice)))
|
bf.WriteUint32(uint32(len(likedBySlice)))
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteBool(liked)
|
bf.WriteBool(liked)
|
||||||
bf.WriteUint32(postData.StampID)
|
bf.WriteUint32(postData.StampID)
|
||||||
bf.WriteUint32(uint32(len(postData.Title)))
|
bf.WriteUint32(uint32(len(str_title)))
|
||||||
bf.WriteBytes([]byte(postData.Title))
|
bf.WriteBytes([]byte(str_title))
|
||||||
bf.WriteUint32(uint32(len(postData.Body)))
|
bf.WriteUint32(uint32(len(str_body)))
|
||||||
bf.WriteBytes([]byte(postData.Body))
|
bf.WriteBytes([]byte(str_body))
|
||||||
}
|
}
|
||||||
if noMsgs {
|
if noMsgs {
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
@@ -1615,6 +1644,19 @@ func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func transformEncoding(rawReader io.Reader, trans transform.Transformer) (string, error) {
|
||||||
|
ret, err := ioutil.ReadAll(transform.NewReader(rawReader, trans))
|
||||||
|
if err == nil {
|
||||||
|
return string(ret), nil
|
||||||
|
} else {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BytesFromShiftJIS(b []byte) (string, error) {
|
||||||
|
return transformEncoding(bytes.NewReader(b), japanese.ShiftJIS.NewDecoder())
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfUpdateGuildMessageBoard)
|
pkt := p.(*mhfpacket.MsgMhfUpdateGuildMessageBoard)
|
||||||
bf := byteframe.NewByteFrameFromBytes(pkt.Request)
|
bf := byteframe.NewByteFrameFromBytes(pkt.Request)
|
||||||
@@ -1623,57 +1665,109 @@ func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch pkt.MessageOp {
|
switch pkt.MessageOp {
|
||||||
case 0: // Create message
|
case 0: // Create message
|
||||||
|
var title_str, body_str string
|
||||||
|
|
||||||
postType := bf.ReadUint32() // 0 = message, 1 = news
|
postType := bf.ReadUint32() // 0 = message, 1 = news
|
||||||
stampId := bf.ReadUint32()
|
stampId := bf.ReadUint32()
|
||||||
titleLength := bf.ReadUint32()
|
titleLength := bf.ReadUint32()
|
||||||
bodyLength := bf.ReadUint32()
|
bodyLength := bf.ReadUint32()
|
||||||
title := bf.ReadBytes(uint(titleLength))
|
title := bf.ReadBytes(uint(titleLength))
|
||||||
body := bf.ReadBytes(uint(bodyLength))
|
body := bf.ReadBytes(uint(bodyLength))
|
||||||
_, err := s.server.db.Exec("INSERT INTO guild_posts (guild_id, author_id, stamp_id, post_type, title, body) VALUES ($1, $2, $3, $4, $5, $6)", guild.ID, s.charID, int(stampId), int(postType), string(title), string(body))
|
|
||||||
|
title_str, err := BytesFromShiftJIS(title)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to add new guild message to db", zap.Error(err))
|
log.Println(err)
|
||||||
|
}
|
||||||
|
fmt.Println(title_str)
|
||||||
|
|
||||||
|
body_str, err = BytesFromShiftJIS(body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
fmt.Println(body_str)
|
||||||
|
|
||||||
|
_, err = s.server.db.Exec("INSERT INTO guild_posts (guild_id, author_id, stamp_id, post_type, title, body) VALUES ($1, $2, $3, $4, $5, $6)", guild.ID, s.charID, int(stampId), int(postType), string(title_str), string(body_str))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to add new guild message to db", zap.Error(err))
|
||||||
}
|
}
|
||||||
// TODO: if there are too many messages, purge excess
|
// TODO: if there are too many messages, purge excess
|
||||||
_, err = s.server.db.Exec("")
|
_, err = s.server.db.Exec("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to remove excess guild messages from db", zap.Error(err))
|
log.Println("Failed to remove excess guild messages from db", zap.Error(err))
|
||||||
}
|
}
|
||||||
case 1: // Delete message
|
case 1: // Delete message
|
||||||
|
var timestampst, timecomp uint64
|
||||||
|
timestampst = 32400
|
||||||
|
|
||||||
postType := bf.ReadUint32()
|
postType := bf.ReadUint32()
|
||||||
timestamp := bf.ReadUint64()
|
timestamp := bf.ReadUint64()
|
||||||
_, err := s.server.db.Exec("DELETE FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timestamp), guild.ID)
|
|
||||||
|
timecomp = timestamp + timestampst
|
||||||
|
_, err := s.server.db.Exec("DELETE FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timecomp), guild.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to delete guild message from db", zap.Error(err))
|
log.Println("Failed to delete guild message from db", zap.Error(err))
|
||||||
}
|
}
|
||||||
case 2: // Update message
|
case 2: // Update message
|
||||||
|
var title_str, body_str string
|
||||||
|
var timestampst, timecomp uint64
|
||||||
|
timestampst = 32400
|
||||||
|
|
||||||
postType := bf.ReadUint32()
|
postType := bf.ReadUint32()
|
||||||
timestamp := bf.ReadUint64()
|
timestamp := bf.ReadUint64()
|
||||||
titleLength := bf.ReadUint32()
|
titleLength := bf.ReadUint32()
|
||||||
bodyLength := bf.ReadUint32()
|
bodyLength := bf.ReadUint32()
|
||||||
title := bf.ReadBytes(uint(titleLength))
|
title := bf.ReadBytes(uint(titleLength))
|
||||||
body := bf.ReadBytes(uint(bodyLength))
|
body := bf.ReadBytes(uint(bodyLength))
|
||||||
_, err := s.server.db.Exec("UPDATE guild_posts SET title = $1, body = $2 WHERE post_type = $3 AND (EXTRACT(epoch FROM created_at)::int) = $4 AND guild_id = $5", string(title), string(body), int(postType), int(timestamp), guild.ID)
|
|
||||||
|
title_str, err := BytesFromShiftJIS(title)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to update guild message in db", zap.Error(err))
|
log.Println(err)
|
||||||
|
}
|
||||||
|
fmt.Println(title_str)
|
||||||
|
|
||||||
|
body_str, err = BytesFromShiftJIS(body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
fmt.Println(body_str)
|
||||||
|
|
||||||
|
timecomp = timestamp + timestampst
|
||||||
|
|
||||||
|
_, err = s.server.db.Exec("UPDATE guild_posts SET title = $1, body = $2 WHERE post_type = $3 AND (EXTRACT(epoch FROM created_at)::int) = $4 AND guild_id = $5", string(title_str), string(body_str), int(postType), int(timecomp), guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to update guild message in db", zap.Error(err))
|
||||||
}
|
}
|
||||||
case 3: // Update stamp
|
case 3: // Update stamp
|
||||||
|
var timestampst, timecomp uint64
|
||||||
|
timestampst = 32400
|
||||||
|
|
||||||
postType := bf.ReadUint32()
|
postType := bf.ReadUint32()
|
||||||
timestamp := bf.ReadUint64()
|
timestamp := bf.ReadUint64()
|
||||||
stampId := bf.ReadUint32()
|
stampId := bf.ReadUint32()
|
||||||
_, err := s.server.db.Exec("UPDATE guild_posts SET stamp_id = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", int(stampId), int(postType), int(timestamp), guild.ID)
|
|
||||||
|
timecomp = timestamp + timestampst
|
||||||
|
|
||||||
|
_, err := s.server.db.Exec("UPDATE guild_posts SET stamp_id = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", int(stampId), int(postType), int(timecomp), guild.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to update guild message stamp in db", zap.Error(err))
|
log.Println("Failed to update guild message stamp in db", zap.Error(err))
|
||||||
}
|
}
|
||||||
case 4: // Like message
|
case 4: // Like message
|
||||||
|
var timestampst, timecomp uint64
|
||||||
|
timestampst = 32400
|
||||||
|
|
||||||
postType := bf.ReadUint32()
|
postType := bf.ReadUint32()
|
||||||
timestamp := bf.ReadUint64()
|
timestamp := bf.ReadUint64()
|
||||||
likeState := bf.ReadBool()
|
likeState := bf.ReadBool()
|
||||||
|
|
||||||
|
timecomp = timestamp + timestampst
|
||||||
|
|
||||||
var likedBy string
|
var likedBy string
|
||||||
err := s.server.db.QueryRow("SELECT liked_by FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timestamp), guild.ID).Scan(&likedBy)
|
err := s.server.db.QueryRow("SELECT liked_by FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timecomp), guild.ID).Scan(&likedBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to get guild message like data from db", zap.Error(err))
|
log.Println("Failed to get guild message like data from db", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
if likeState {
|
if likeState {
|
||||||
if len(likedBy) == 0 {
|
if len(likedBy) == 0 {
|
||||||
@@ -1681,9 +1775,9 @@ func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
} else {
|
} else {
|
||||||
likedBy += "," + strconv.Itoa(int(s.charID))
|
likedBy += "," + strconv.Itoa(int(s.charID))
|
||||||
}
|
}
|
||||||
_, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timestamp), guild.ID)
|
_, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timecomp), guild.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to like guild message in db", zap.Error(err))
|
log.Println("Failed to like guild message in db", zap.Error(err))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
likedBySlice := strings.Split(likedBy, ",")
|
likedBySlice := strings.Split(likedBy, ",")
|
||||||
@@ -1694,9 +1788,9 @@ func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
likedBy = strings.Join(likedBySlice, ",")
|
likedBy = strings.Join(likedBySlice, ",")
|
||||||
_, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timestamp), guild.ID)
|
_, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timecomp), guild.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to unlike guild message in db", zap.Error(err))
|
log.Println("Failed to unlike guild message in db", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1705,15 +1799,15 @@ func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
var newPosts int
|
var newPosts int
|
||||||
err := s.server.db.QueryRow("SELECT (EXTRACT(epoch FROM guild_post_checked)::int) FROM characters WHERE id = $1", s.charID).Scan(&timeChecked)
|
err := s.server.db.QueryRow("SELECT (EXTRACT(epoch FROM guild_post_checked)::int) FROM characters WHERE id = $1", s.charID).Scan(&timeChecked)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to get last guild post check timestamp from db", zap.Error(err))
|
log.Println("Failed to get last guild post check timestamp from db", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
_, err = s.server.db.Exec("UPDATE characters SET guild_post_checked = $1 WHERE id = $2", time.Now(), s.charID)
|
_, err = s.server.db.Exec("UPDATE characters SET guild_post_checked = $1 WHERE id = $2", time.Now(), s.charID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to update guild post check timestamp in db", zap.Error(err))
|
log.Println("Failed to update guild post check timestamp in db", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
err = s.server.db.QueryRow("SELECT COUNT(*) FROM guild_posts WHERE guild_id = $1 AND (EXTRACT(epoch FROM created_at)::int) > $2", guild.ID, timeChecked).Scan(&newPosts)
|
err = s.server.db.QueryRow("SELECT COUNT(*) FROM guild_posts WHERE guild_id = $1 AND (EXTRACT(epoch FROM created_at)::int) > $2", guild.ID, timeChecked).Scan(&newPosts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to check for new guild posts in db", zap.Error(err))
|
log.Println("Failed to check for new guild posts in db", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
if newPosts > 0 {
|
if newPosts > 0 {
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||||
|
|||||||
@@ -2,16 +2,28 @@ package channelserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
//"github.com/Solenataris/Erupe/common/stringsupport"
|
//"github.com/Solenataris/Erupe/common/stringsupport"
|
||||||
"github.com/Solenataris/Erupe/network/mhfpacket"
|
|
||||||
"github.com/Andoryuuta/byteframe"
|
"github.com/Andoryuuta/byteframe"
|
||||||
|
"github.com/Solenataris/Erupe/network/mhfpacket"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
"github.com/sachaos/lottery"
|
"github.com/sachaos/lottery"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func contains(s []string, str string) bool {
|
||||||
|
for _, v := range s {
|
||||||
|
if v == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateShop)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateShop)
|
||||||
// SHOP TYPES:
|
// SHOP TYPES:
|
||||||
@@ -150,19 +162,27 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
shopEntries, err := s.server.db.Query("SELECT itemhash,itemID,Points,TradeQuantity,rankReqLow,rankReqHigh,rankReqG,storeLevelReq,maximumQuantity,boughtQuantity,roadFloorsRequired,weeklyFatalisKills FROM normal_shop_items WHERE shoptype=$1 AND shopid=$2", pkt.ShopType, pkt.ShopID)
|
_, week := time.Now().ISOWeek()
|
||||||
|
season := fmt.Sprintf("%d", week%4)
|
||||||
|
shopEntries, err := s.server.db.Query("SELECT itemhash,itemID,Points,TradeQuantity,rankReqLow,rankReqHigh,rankReqG,storeLevelReq,maximumQuantity,boughtQuantity,roadFloorsRequired,weeklyFatalisKills, COALESCE(enable_weeks, '') FROM normal_shop_items WHERE shoptype=$1 AND shopid=$2", pkt.ShopType, pkt.ShopID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var ItemHash, entryCount int
|
var ItemHash, entryCount int
|
||||||
var itemID, Points, TradeQuantity, rankReqLow, rankReqHigh, rankReqG, storeLevelReq, maximumQuantity, boughtQuantity, roadFloorsRequired, weeklyFatalisKills, charQuantity uint16
|
var itemID, Points, TradeQuantity, rankReqLow, rankReqHigh, rankReqG, storeLevelReq, maximumQuantity, boughtQuantity, roadFloorsRequired, weeklyFatalisKills, charQuantity uint16
|
||||||
|
var itemWeeks string
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
resp.WriteUint32(0) // total defs
|
resp.WriteUint32(0) // total defs
|
||||||
for shopEntries.Next() {
|
for shopEntries.Next() {
|
||||||
err = shopEntries.Scan(&ItemHash, &itemID, &Points, &TradeQuantity, &rankReqLow, &rankReqHigh, &rankReqG, &storeLevelReq, &maximumQuantity, &boughtQuantity, &roadFloorsRequired, &weeklyFatalisKills)
|
err = shopEntries.Scan(&ItemHash, &itemID, &Points, &TradeQuantity, &rankReqLow, &rankReqHigh, &rankReqG, &storeLevelReq, &maximumQuantity, &boughtQuantity, &roadFloorsRequired, &weeklyFatalisKills, &itemWeeks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(itemWeeks) > 0 && !contains(strings.Split(itemWeeks, ","), season) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
resp.WriteUint32(uint32(ItemHash))
|
resp.WriteUint32(uint32(ItemHash))
|
||||||
resp.WriteUint16(0) // unk, always 0 in existing packets
|
resp.WriteUint16(0) // unk, always 0 in existing packets
|
||||||
resp.WriteUint16(itemID)
|
resp.WriteUint16(itemID)
|
||||||
@@ -175,9 +195,13 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
resp.WriteUint16(storeLevelReq)
|
resp.WriteUint16(storeLevelReq)
|
||||||
resp.WriteUint16(maximumQuantity)
|
resp.WriteUint16(maximumQuantity)
|
||||||
if maximumQuantity > 0 {
|
if maximumQuantity > 0 {
|
||||||
err = s.server.db.QueryRow("SELECT COALESCE(usedquantity,0) FROM shop_item_state WHERE itemhash=$1 AND char_id=$2", ItemHash, s.charID).Scan(&charQuantity)
|
var itemWeek int
|
||||||
|
err = s.server.db.QueryRow("SELECT COALESCE(usedquantity,0), COALESCE(week,-1) FROM shop_item_state WHERE itemhash=$1 AND char_id=$2", ItemHash, s.charID).Scan(&charQuantity, &itemWeek)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.WriteUint16(0)
|
resp.WriteUint16(0)
|
||||||
|
} else if pkt.ShopID == 7 && itemWeek >= 0 && itemWeek != week {
|
||||||
|
clearShopItemState(s, s.charID, uint32(ItemHash))
|
||||||
|
resp.WriteUint16(0)
|
||||||
} else {
|
} else {
|
||||||
resp.WriteUint16(charQuantity)
|
resp.WriteUint16(charQuantity)
|
||||||
}
|
}
|
||||||
@@ -200,6 +224,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
_, week := time.Now().ISOWeek()
|
||||||
// writing out to an editable shop enumeration
|
// writing out to an editable shop enumeration
|
||||||
pkt := p.(*mhfpacket.MsgMhfAcquireExchangeShop)
|
pkt := p.(*mhfpacket.MsgMhfAcquireExchangeShop)
|
||||||
if pkt.DataSize == 10 {
|
if pkt.DataSize == 10 {
|
||||||
@@ -207,10 +232,10 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
_ = bf.ReadUint16() // unk, always 1 in examples
|
_ = bf.ReadUint16() // unk, always 1 in examples
|
||||||
itemHash := bf.ReadUint32()
|
itemHash := bf.ReadUint32()
|
||||||
buyCount := bf.ReadUint32()
|
buyCount := bf.ReadUint32()
|
||||||
_, err := s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity)
|
_, err := s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity, week)
|
||||||
VALUES ($1,$2,$3) ON CONFLICT (char_id, itemhash)
|
VALUES ($1,$2,$3,$4) ON CONFLICT (char_id, itemhash)
|
||||||
DO UPDATE SET usedquantity = shop_item_state.usedquantity + $3
|
DO UPDATE SET usedquantity = shop_item_state.usedquantity + $3
|
||||||
WHERE EXCLUDED.char_id=$1 AND EXCLUDED.itemhash=$2`, s.charID, itemHash, buyCount)
|
WHERE EXCLUDED.char_id=$1 AND EXCLUDED.itemhash=$2`, s.charID, itemHash, buyCount, week)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to update shop_item_state in db", zap.Error(err))
|
s.logger.Fatal("Failed to update shop_item_state in db", zap.Error(err))
|
||||||
}
|
}
|
||||||
@@ -218,6 +243,13 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clearShopItemState(s *Session, charId uint32, itemHash uint32) {
|
||||||
|
_, err := s.server.db.Exec(`DELETE FROM shop_item_state WHERE char_id=$1 AND itemhash=$2`, charId, itemHash)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Fatal("Failed to delete shop_item_state in db", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) {
|
||||||
// returns number of times the gacha was played, will need persistent db stuff
|
// returns number of times the gacha was played, will need persistent db stuff
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory)
|
pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory)
|
||||||
|
|||||||
@@ -9,16 +9,36 @@ import (
|
|||||||
"github.com/Solenataris/Erupe/config"
|
"github.com/Solenataris/Erupe/config"
|
||||||
"github.com/Solenataris/Erupe/network/binpacket"
|
"github.com/Solenataris/Erupe/network/binpacket"
|
||||||
"github.com/Solenataris/Erupe/network/mhfpacket"
|
"github.com/Solenataris/Erupe/network/mhfpacket"
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/Solenataris/Erupe/server/discordbot"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type StageIdType = string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GlobalStage is the stage that is used for all users.
|
||||||
|
MezeportaStageId StageIdType = "sl1Ns200p0a0u0"
|
||||||
|
GuildHallLv1StageId StageIdType = "sl1Ns202p0a0u0"
|
||||||
|
GuildHallLv2StageId StageIdType = "sl1Ns203p0a0u0"
|
||||||
|
GuildHallLv3StageId StageIdType = "sl1Ns204p0a0u0"
|
||||||
|
PugiFarmStageId StageIdType = "sl1Ns205p0a0u0"
|
||||||
|
RastaBarStageId StageIdType = "sl1Ns211p0a0u0"
|
||||||
|
PalloneCaravanStageId StageIdType = "sl1Ns260p0a0u0"
|
||||||
|
GookFarmStageId StageIdType = "sl1Ns265p0a0u0"
|
||||||
|
DivaFountainStageId StageIdType = "sl2Ns379p0a0u0"
|
||||||
|
DivaHallStageId StageIdType = "sl1Ns445p0a0u0"
|
||||||
|
MezFesStageId StageIdType = "sl1Ns462p0a0u0"
|
||||||
|
)
|
||||||
|
|
||||||
// Config struct allows configuring the server.
|
// Config struct allows configuring the server.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
DB *sqlx.DB
|
DB *sqlx.DB
|
||||||
|
DiscordBot *discordbot.DiscordBot
|
||||||
ErupeConfig *config.Config
|
ErupeConfig *config.Config
|
||||||
|
Name string
|
||||||
|
Enable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map key type for a user binary part.
|
// Map key type for a user binary part.
|
||||||
@@ -37,7 +57,6 @@ type Server struct {
|
|||||||
deleteConns chan net.Conn
|
deleteConns chan net.Conn
|
||||||
sessions map[net.Conn]*Session
|
sessions map[net.Conn]*Session
|
||||||
listener net.Listener // Listener that is created when Server.Start is called.
|
listener net.Listener // Listener that is created when Server.Start is called.
|
||||||
|
|
||||||
isShuttingDown bool
|
isShuttingDown bool
|
||||||
|
|
||||||
stagesLock sync.RWMutex
|
stagesLock sync.RWMutex
|
||||||
@@ -52,7 +71,10 @@ type Server struct {
|
|||||||
semaphore map[string]*Semaphore
|
semaphore map[string]*Semaphore
|
||||||
|
|
||||||
// Discord chat integration
|
// Discord chat integration
|
||||||
discordSession *discordgo.Session
|
discordBot *discordbot.DiscordBot
|
||||||
|
|
||||||
|
name string
|
||||||
|
enable bool
|
||||||
|
|
||||||
raviente *Raviente
|
raviente *Raviente
|
||||||
}
|
}
|
||||||
@@ -124,7 +146,10 @@ func NewServer(config *Config) *Server {
|
|||||||
stages: make(map[string]*Stage),
|
stages: make(map[string]*Stage),
|
||||||
userBinaryParts: make(map[userBinaryPartID][]byte),
|
userBinaryParts: make(map[userBinaryPartID][]byte),
|
||||||
semaphore: make(map[string]*Semaphore),
|
semaphore: make(map[string]*Semaphore),
|
||||||
discordSession: nil,
|
discordBot: config.DiscordBot,
|
||||||
|
name: config.Name,
|
||||||
|
enable: config.Enable,
|
||||||
|
raviente: NewRaviente(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mezeporta
|
// Mezeporta
|
||||||
@@ -145,7 +170,7 @@ func NewServer(config *Config) *Server {
|
|||||||
// Rasta bar stage
|
// Rasta bar stage
|
||||||
s.stages["sl1Ns211p0a0u0"] = NewStage("sl1Ns211p0a0u0")
|
s.stages["sl1Ns211p0a0u0"] = NewStage("sl1Ns211p0a0u0")
|
||||||
|
|
||||||
// Carvane
|
// Pallone Carvan
|
||||||
s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0")
|
s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0")
|
||||||
|
|
||||||
// Gook Farm
|
// Gook Farm
|
||||||
@@ -160,16 +185,6 @@ func NewServer(config *Config) *Server {
|
|||||||
// MezFes
|
// MezFes
|
||||||
s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0")
|
s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0")
|
||||||
|
|
||||||
// Create the discord session, (not actually connecting to discord servers yet).
|
|
||||||
if s.erupeConfig.Discord.Enabled {
|
|
||||||
ds, err := discordgo.New("Bot " + s.erupeConfig.Discord.BotToken)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatal("Error creating Discord session.", zap.Error(err))
|
|
||||||
}
|
|
||||||
ds.AddHandler(s.onDiscordMessage)
|
|
||||||
s.discordSession = ds
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,12 +200,8 @@ func (s *Server) Start(port int) error {
|
|||||||
go s.manageSessions()
|
go s.manageSessions()
|
||||||
|
|
||||||
// Start the discord bot for chat integration.
|
// Start the discord bot for chat integration.
|
||||||
if s.erupeConfig.Discord.Enabled {
|
if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
|
||||||
err = s.discordSession.Open()
|
s.discordBot.Session.AddHandler(s.onDiscordMessage)
|
||||||
if err != nil {
|
|
||||||
s.logger.Warn("Error opening Discord session.", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -203,11 +214,8 @@ func (s *Server) Shutdown() {
|
|||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
|
||||||
s.listener.Close()
|
s.listener.Close()
|
||||||
close(s.acceptConns)
|
|
||||||
|
|
||||||
if s.erupeConfig.Discord.Enabled {
|
close(s.acceptConns)
|
||||||
s.discordSession.Close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) acceptClients() {
|
func (s *Server) acceptClients() {
|
||||||
@@ -289,7 +297,7 @@ func (s *Server) BroadcastChatMessage(message string) {
|
|||||||
Type: 5,
|
Type: 5,
|
||||||
Flags: 0x80,
|
Flags: 0x80,
|
||||||
Message: message,
|
Message: message,
|
||||||
SenderName: "Erupe",
|
SenderName: s.name,
|
||||||
}
|
}
|
||||||
msgBinChat.Build(bf)
|
msgBinChat.Build(bf)
|
||||||
|
|
||||||
@@ -300,6 +308,13 @@ func (s *Server) BroadcastChatMessage(message string) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) DiscordChannelSend(charName string, content string) {
|
||||||
|
if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
|
||||||
|
message := fmt.Sprintf("**%s** : %s", charName, content)
|
||||||
|
s.discordBot.RealtimeChannelSend(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) FindSessionByCharID(charID uint32) *Session {
|
func (s *Server) FindSessionByCharID(charID uint32) *Session {
|
||||||
s.stagesLock.RLock()
|
s.stagesLock.RLock()
|
||||||
defer s.stagesLock.RUnlock()
|
defer s.stagesLock.RUnlock()
|
||||||
@@ -316,3 +331,21 @@ func (s *Server) FindSessionByCharID(charID uint32) *Session {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) FindStageObjectByChar(charID uint32) *StageObject {
|
||||||
|
s.stagesLock.RLock()
|
||||||
|
defer s.stagesLock.RUnlock()
|
||||||
|
for _, stage := range s.stages {
|
||||||
|
stage.RLock()
|
||||||
|
for objId := range stage.objects {
|
||||||
|
obj := stage.objects[objId]
|
||||||
|
if obj.ownerCharID == charID {
|
||||||
|
stage.RUnlock()
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package channelserver
|
package channelserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Andoryuuta/byteframe"
|
"github.com/Andoryuuta/byteframe"
|
||||||
"github.com/Solenataris/Erupe/network/mhfpacket"
|
"github.com/Solenataris/Erupe/network/mhfpacket"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// StageObject holds infomation about a specific stage object.
|
// StageObject holds infomation about a specific stage object.
|
||||||
@@ -55,6 +58,7 @@ type Stage struct {
|
|||||||
maxPlayers uint16
|
maxPlayers uint16
|
||||||
hasDeparted bool
|
hasDeparted bool
|
||||||
password string
|
password string
|
||||||
|
createdAt string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStage creates a new stage with intialized values.
|
// NewStage creates a new stage with intialized values.
|
||||||
@@ -68,6 +72,7 @@ func NewStage(ID string) *Stage {
|
|||||||
maxPlayers: 4,
|
maxPlayers: 4,
|
||||||
gameObjectCount: 1,
|
gameObjectCount: 1,
|
||||||
objectList: make(map[uint8]*ObjectMap),
|
objectList: make(map[uint8]*ObjectMap),
|
||||||
|
createdAt: time.Now().Format("01-02-2006 15:04:05"),
|
||||||
}
|
}
|
||||||
s.InitObjectList()
|
s.InitObjectList()
|
||||||
return s
|
return s
|
||||||
@@ -104,6 +109,47 @@ func (s *Stage) InitObjectList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Stage) isCharInQuestByID(charID uint32) bool {
|
||||||
|
if _, exists := s.reservedClientSlots[charID]; exists {
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stage) isQuest() bool {
|
||||||
|
return len(s.reservedClientSlots) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stage *Stage) GetName() string {
|
||||||
|
switch stage.id {
|
||||||
|
case MezeportaStageId:
|
||||||
|
return "Mezeporta"
|
||||||
|
case GuildHallLv1StageId:
|
||||||
|
return "Guild Hall Lv1"
|
||||||
|
case GuildHallLv2StageId:
|
||||||
|
return "Guild Hall Lv2"
|
||||||
|
case GuildHallLv3StageId:
|
||||||
|
return "Guild Hall Lv3"
|
||||||
|
case PugiFarmStageId:
|
||||||
|
return "Pugi Farm"
|
||||||
|
case RastaBarStageId:
|
||||||
|
return "Rasta Bar"
|
||||||
|
case PalloneCaravanStageId:
|
||||||
|
return "Pallone Caravan"
|
||||||
|
case GookFarmStageId:
|
||||||
|
return "Gook Farm"
|
||||||
|
case DivaFountainStageId:
|
||||||
|
return "Diva Fountain"
|
||||||
|
case DivaHallStageId:
|
||||||
|
return "Diva Hall"
|
||||||
|
case MezFesStageId:
|
||||||
|
return "Mez Fes"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Stage) GetNewObjectID(CharID uint32) uint32 {
|
func (s *Stage) GetNewObjectID(CharID uint32) uint32 {
|
||||||
ObjId := uint8(0)
|
ObjId := uint8(0)
|
||||||
for seq := uint8(0x7f); seq > uint8(0); seq-- {
|
for seq := uint8(0x7f); seq > uint8(0); seq-- {
|
||||||
|
|||||||
120
Erupe/server/discordbot/discord_bot.go
Normal file
120
Erupe/server/discordbot/discord_bot.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package discordbot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/Solenataris/Erupe/config"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiscordBot struct {
|
||||||
|
Session *discordgo.Session
|
||||||
|
config *config.Config
|
||||||
|
logger *zap.Logger
|
||||||
|
MainGuild *discordgo.Guild
|
||||||
|
RealtimeChannel *discordgo.Channel
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscordBotOptions struct {
|
||||||
|
Config *config.Config
|
||||||
|
Logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error) {
|
||||||
|
session, err := discordgo.New("Bot " + options.Config.Discord.BotToken)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
options.Logger.Fatal("Discord failed", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mainGuild, err := session.Guild(options.Config.Discord.ServerID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
options.Logger.Fatal("Discord failed to get main guild", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
options.Logger.Fatal("Discord failed to create realtimeChannel", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
discordBot = &DiscordBot{
|
||||||
|
config: options.Config,
|
||||||
|
logger: options.Logger,
|
||||||
|
Session: session,
|
||||||
|
MainGuild: mainGuild,
|
||||||
|
RealtimeChannel: realtimeChannel,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bot *DiscordBot) Start() (err error) {
|
||||||
|
err = bot.Session.Open()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bot *DiscordBot) FindRoleByID(id string) *discordgo.Role {
|
||||||
|
for _, role := range bot.MainGuild.Roles {
|
||||||
|
if role.ID == id {
|
||||||
|
return role
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace all mentions to real name from the message.
|
||||||
|
func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
|
||||||
|
userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`)
|
||||||
|
emojiRegex := regexp.MustCompile(`(?:<a?)?:(\w+):(?:\d{18}>)?`)
|
||||||
|
roleRegex := regexp.MustCompile(`<@&(\d{17,19})>`)
|
||||||
|
|
||||||
|
result := ReplaceTextAll(message, userRegex, func(userId string) string {
|
||||||
|
user, err := bot.Session.User(userId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "@unknown" // @Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return "@" + user.Username
|
||||||
|
})
|
||||||
|
|
||||||
|
result = ReplaceTextAll(result, emojiRegex, func(emojiName string) string {
|
||||||
|
return ":" + emojiName + ":"
|
||||||
|
})
|
||||||
|
|
||||||
|
result = ReplaceTextAll(result, roleRegex, func(roleId string) string {
|
||||||
|
role := bot.FindRoleByID(roleId)
|
||||||
|
|
||||||
|
if role != nil {
|
||||||
|
return "@!" + role.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return "@!unknown"
|
||||||
|
})
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {
|
||||||
|
_, err = bot.Session.ChannelMessageSend(bot.RealtimeChannel.ID, message)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string {
|
||||||
|
result := regex.ReplaceAllFunc([]byte(text), func(s []byte) []byte {
|
||||||
|
input := regex.ReplaceAllString(string(s), `$1`)
|
||||||
|
|
||||||
|
return []byte(handler(input))
|
||||||
|
})
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user