mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-14 16:04:38 +01:00
Compare commits
10 Commits
feature/ev
...
test/aobp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce364720de | ||
|
|
c4ec2efde5 | ||
|
|
aad3b088b9 | ||
|
|
dd36f367a9 | ||
|
|
8f3624d589 | ||
|
|
8d1c6a79e2 | ||
|
|
d83c784452 | ||
|
|
ee7b099deb | ||
|
|
ef3212da11 | ||
|
|
3d0114ce22 |
@@ -28,7 +28,7 @@ If you want to modify or compile Erupe yourself, please read on.
|
||||
## Installation
|
||||
|
||||
1. Bring up a fresh database by using the [backup file attached with the latest release](https://github.com/ZeruLight/Erupe/releases/latest/download/SCHEMA.sql).
|
||||
2. Run each script under [patch-schema](./patch-schema) as they introduce newer schema.
|
||||
2. Run each script under [patch-schema](./schemas/patch-schema) as they introduce newer schema.
|
||||
3. Edit [config.json](./config.json) such that the database password matches your PostgreSQL setup.
|
||||
4. Run `go build` or `go run .` to compile Erupe.
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ func (m *MsgMhfAcquireCafeItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl
|
||||
m.ItemType = bf.ReadUint16()
|
||||
m.ItemID = bf.ReadUint16()
|
||||
m.Quant = bf.ReadUint16()
|
||||
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||
if _config.ErupeConfig.RealClientMode >= _config.G6 {
|
||||
m.PointCost = bf.ReadUint32()
|
||||
} else {
|
||||
m.PointCost = uint32(bf.ReadUint16())
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
// MsgMhfAcquireItem represents the MSG_MHF_ACQUIRE_ITEM
|
||||
type MsgMhfAcquireItem struct {
|
||||
AckHandle uint32
|
||||
RewardIDs []uint32
|
||||
Unk0 uint16
|
||||
Length uint16
|
||||
Unk1 []uint32
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
@@ -22,10 +24,10 @@ func (m *MsgMhfAcquireItem) Opcode() network.PacketID {
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgMhfAcquireItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
bf.ReadUint16() // Zeroed
|
||||
ids := bf.ReadUint16()
|
||||
for i := uint16(0); i < ids; i++ {
|
||||
m.RewardIDs = append(m.RewardIDs, bf.ReadUint32())
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
m.Length = bf.ReadUint16()
|
||||
for i := 0; i < int(m.Length); i++ {
|
||||
m.Unk1 = append(m.Unk1, bf.ReadUint32())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package mhfpacket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"erupe-ce/common/bfutil"
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
@@ -10,9 +9,10 @@ import (
|
||||
|
||||
// MsgMhfApplyCampaign represents the MSG_MHF_APPLY_CAMPAIGN
|
||||
type MsgMhfApplyCampaign struct {
|
||||
AckHandle uint32
|
||||
CampaignID uint32
|
||||
Code string
|
||||
AckHandle uint32
|
||||
Unk0 uint32
|
||||
Unk1 uint16
|
||||
Unk2 []byte
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
@@ -23,9 +23,9 @@ func (m *MsgMhfApplyCampaign) Opcode() network.PacketID {
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgMhfApplyCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.CampaignID = bf.ReadUint32()
|
||||
bf.ReadUint16() // Zeroed
|
||||
m.Code = string(bfutil.UpToNull(bf.ReadBytes(16)))
|
||||
m.Unk0 = bf.ReadUint32()
|
||||
m.Unk1 = bf.ReadUint16()
|
||||
m.Unk2 = bf.ReadBytes(16)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
// MsgMhfEnumerateCampaign represents the MSG_MHF_ENUMERATE_CAMPAIGN
|
||||
type MsgMhfEnumerateCampaign struct {
|
||||
AckHandle uint32
|
||||
NullPadding1 uint16 // 0 in z2
|
||||
NullPadding2 uint16 // 0 in z2
|
||||
AckHandle uint32
|
||||
Unk0 uint16
|
||||
Unk1 uint16
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
@@ -21,15 +21,15 @@ func (m *MsgMhfEnumerateCampaign) Opcode() network.PacketID {
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgMhfEnumerateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.NullPadding1 = bf.ReadUint16()
|
||||
m.NullPadding2 = bf.ReadUint16()
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
m.Unk1 = bf.ReadUint16()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build builds a binary packet from the current data.
|
||||
func (m *MsgMhfEnumerateCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
bf.WriteUint32(m.AckHandle)
|
||||
bf.WriteUint16(m.NullPadding1)
|
||||
bf.WriteUint16(m.NullPadding2)
|
||||
bf.WriteUint16(m.Unk0)
|
||||
bf.WriteUint16(m.Unk1)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
// MsgMhfEnumerateItem represents the MSG_MHF_ENUMERATE_ITEM
|
||||
type MsgMhfEnumerateItem struct {
|
||||
AckHandle uint32
|
||||
Unk0 uint16
|
||||
Unk1 uint16
|
||||
CampaignID uint32
|
||||
}
|
||||
|
||||
@@ -22,8 +24,8 @@ func (m *MsgMhfEnumerateItem) Opcode() network.PacketID {
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgMhfEnumerateItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
bf.ReadUint16() // Zeroed
|
||||
bf.ReadUint16() // Always 2
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
m.Unk1 = bf.ReadUint16()
|
||||
m.CampaignID = bf.ReadUint32()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
// MsgMhfStateCampaign represents the MSG_MHF_STATE_CAMPAIGN
|
||||
type MsgMhfStateCampaign struct {
|
||||
AckHandle uint32
|
||||
CampaignID uint32
|
||||
NullPadding uint16
|
||||
AckHandle uint32
|
||||
CampaignID uint32
|
||||
Unk1 uint16
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
@@ -24,7 +24,7 @@ func (m *MsgMhfStateCampaign) Opcode() network.PacketID {
|
||||
func (m *MsgMhfStateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.CampaignID = bf.ReadUint32()
|
||||
m.NullPadding = bf.ReadUint16() //0 in Z2
|
||||
m.Unk1 = bf.ReadUint16()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,12 @@ import (
|
||||
// MsgMhfTransferItem represents the MSG_MHF_TRANSFER_ITEM
|
||||
type MsgMhfTransferItem struct {
|
||||
AckHandle uint32
|
||||
QuestID uint32
|
||||
ItemType uint8
|
||||
Quantity uint16
|
||||
// looking at packets, these were static across sessions and did not actually
|
||||
// correlate with any item IDs that would make sense to get after quests so
|
||||
// I have no idea what this actually does
|
||||
Unk0 uint32
|
||||
Unk1 uint8
|
||||
Unk2 uint16
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
@@ -24,10 +27,10 @@ func (m *MsgMhfTransferItem) Opcode() network.PacketID {
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgMhfTransferItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.QuestID = bf.ReadUint32()
|
||||
m.ItemType = bf.ReadUint8()
|
||||
m.Unk0 = bf.ReadUint32()
|
||||
m.Unk1 = bf.ReadUint8()
|
||||
bf.ReadUint8() // Zeroed
|
||||
m.Quantity = bf.ReadUint16()
|
||||
m.Unk2 = bf.ReadUint16()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,66 +0,0 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.campaigns (
|
||||
id SERIAL PRIMARY KEY,
|
||||
min_hr INTEGER,
|
||||
max_hr INTEGER,
|
||||
min_sr INTEGER,
|
||||
max_sr INTEGER,
|
||||
min_gr INTEGER,
|
||||
max_gr INTEGER,
|
||||
reward_type INTEGER,
|
||||
stamps INTEGER,
|
||||
unk INTEGER,
|
||||
background_id INTEGER,
|
||||
start_time TIMESTAMP WITH TIME ZONE,
|
||||
end_time TIMESTAMP WITH TIME ZONE,
|
||||
title TEXT,
|
||||
reward TEXT,
|
||||
link TEXT,
|
||||
code_prefix TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.campaign_categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
type INTEGER,
|
||||
title TEXT,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.campaign_category_links (
|
||||
id SERIAL PRIMARY KEY,
|
||||
campaign_id INTEGER,
|
||||
category_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.campaign_rewards (
|
||||
id SERIAL PRIMARY KEY,
|
||||
campaign_id INTEGER,
|
||||
item_type INTEGER,
|
||||
quantity INTEGER,
|
||||
item_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.campaign_rewards_claimed (
|
||||
character_id INTEGER,
|
||||
reward_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.campaign_state (
|
||||
id SERIAL PRIMARY KEY,
|
||||
campaign_id INTEGER,
|
||||
character_id INTEGER,
|
||||
code TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.campaign_codes (
|
||||
code TEXT,
|
||||
multi BOOLEAN
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.campaign_quest (
|
||||
campaign_id INTEGER,
|
||||
character_id INTEGER
|
||||
);
|
||||
|
||||
END;
|
||||
@@ -3,4 +3,4 @@ BEGIN;
|
||||
ALTER TABLE IF EXISTS public.stamps RENAME hl_next TO hl_checked;
|
||||
ALTER TABLE IF EXISTS public.stamps RENAME ex_next TO ex_checked;
|
||||
|
||||
END;
|
||||
END;
|
||||
5
schemas/patch-schema/25-fix-rasta-id.sql
Normal file
5
schemas/patch-schema/25-fix-rasta-id.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE SEQUENCE IF NOT EXISTS public.rasta_id_seq;
|
||||
|
||||
END;
|
||||
5
schemas/patch-schema/26-fix-mail.sql
Normal file
5
schemas/patch-schema/26-fix-mail.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE mail ADD COLUMN IF NOT EXISTS is_sys_message BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
END;
|
||||
@@ -183,7 +183,6 @@ func logoutPlayer(s *Session) {
|
||||
delete(s.server.sessions, s.rawConn)
|
||||
}
|
||||
s.rawConn.Close()
|
||||
delete(s.server.objectIDs, s)
|
||||
s.server.Unlock()
|
||||
|
||||
for _, stage := range s.server.stages {
|
||||
@@ -640,6 +639,11 @@ func handleMsgSysInfokyserver(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetCaUniqueID(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfTransferItem)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumeratePrice)
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
@@ -10,68 +10,51 @@ import (
|
||||
)
|
||||
|
||||
type CampaignEvent struct {
|
||||
ID uint32 `db:"id"`
|
||||
MinHR int16 `db:"min_hr"`
|
||||
MaxHR int16 `db:"max_hr"`
|
||||
MinSR int16 `db:"min_sr"`
|
||||
MaxSR int16 `db:"max_sr"`
|
||||
MinGR int16 `db:"min_gr"`
|
||||
MaxGR int16 `db:"max_gr"`
|
||||
RewardType uint16 `db:"reward_type"`
|
||||
Stamps uint8 `db:"stamps"`
|
||||
Unk uint8 `db:"unk"`
|
||||
BackgroundID uint16 `db:"background_id"`
|
||||
Start time.Time `db:"start_time"`
|
||||
End time.Time `db:"end_time"`
|
||||
Title string `db:"title"`
|
||||
Reward string `db:"reward"`
|
||||
Link string `db:"link"`
|
||||
Prefix string `db:"code_prefix"`
|
||||
ID uint32
|
||||
Unk0 uint32
|
||||
MinHR int16
|
||||
MaxHR int16
|
||||
MinSR int16
|
||||
MaxSR int16
|
||||
MinGR int16
|
||||
MaxGR int16
|
||||
Unk1 uint16
|
||||
Unk2 uint8
|
||||
Unk3 uint8
|
||||
Unk4 uint16
|
||||
Unk5 uint16
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Unk6 uint8
|
||||
String0 string
|
||||
String1 string
|
||||
String2 string
|
||||
String3 string
|
||||
Link string
|
||||
Prefix string
|
||||
Categories []uint16
|
||||
}
|
||||
|
||||
type CampaignCategory struct {
|
||||
ID uint16 `db:"id"`
|
||||
Type uint8 `db:"type"`
|
||||
Title string `db:"title"`
|
||||
Description string `db:"description"`
|
||||
ID uint16
|
||||
Type uint8
|
||||
Title string
|
||||
Description string
|
||||
}
|
||||
|
||||
type CampaignLink struct {
|
||||
CategoryID uint16 `db:"category_id"`
|
||||
CampaignID uint32 `db:"campaign_id"`
|
||||
}
|
||||
|
||||
type CampaignReward struct {
|
||||
ID uint32 `db:"id"`
|
||||
ItemType uint16 `db:"item_type"`
|
||||
Quantity uint16 `db:"quantity"`
|
||||
ItemID uint16 `db:"item_id"`
|
||||
Deadline time.Time `db:"deadline"`
|
||||
CategoryID uint16
|
||||
CampaignID uint32
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateCampaign)
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
var events []CampaignEvent
|
||||
var categories []CampaignCategory
|
||||
events := []CampaignEvent{}
|
||||
categories := []CampaignCategory{}
|
||||
var campaignLinks []CampaignLink
|
||||
|
||||
err := s.server.db.Select(&events, "SELECT id,min_hr,max_hr,min_sr,max_sr,min_gr,max_gr,reward_type,stamps,unk,background_id,start_time,end_time,title,reward,link,code_prefix FROM campaigns")
|
||||
if err != nil {
|
||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
err = s.server.db.Select(&categories, "SELECT id, type, title, description FROM campaign_categories")
|
||||
if err != nil {
|
||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
err = s.server.db.Select(&campaignLinks, "SELECT campaign_id, category_id FROM campaign_category_links")
|
||||
if err != nil {
|
||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
if len(events) > 255 {
|
||||
bf.WriteUint8(255)
|
||||
bf.WriteUint16(uint16(len(events)))
|
||||
@@ -80,7 +63,7 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
for _, event := range events {
|
||||
bf.WriteUint32(event.ID)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(event.Unk0)
|
||||
bf.WriteInt16(event.MinHR)
|
||||
bf.WriteInt16(event.MaxHR)
|
||||
bf.WriteInt16(event.MinSR)
|
||||
@@ -89,23 +72,22 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteInt16(event.MinGR)
|
||||
bf.WriteInt16(event.MaxGR)
|
||||
}
|
||||
bf.WriteUint16(event.RewardType)
|
||||
bf.WriteUint8(event.Stamps)
|
||||
bf.WriteUint8(event.Unk) // Related to stamp count
|
||||
bf.WriteUint16(event.BackgroundID)
|
||||
bf.WriteUint16(0)
|
||||
bf.WriteUint16(event.Unk1)
|
||||
bf.WriteUint8(event.Unk2)
|
||||
bf.WriteUint8(event.Unk3)
|
||||
bf.WriteUint16(event.Unk4)
|
||||
bf.WriteUint16(event.Unk5)
|
||||
bf.WriteUint32(uint32(event.Start.Unix()))
|
||||
bf.WriteUint32(uint32(event.End.Unix()))
|
||||
if event.End.After(time.Now()) {
|
||||
bf.WriteBool(true)
|
||||
} else {
|
||||
bf.WriteBool(false)
|
||||
}
|
||||
ps.Uint8(bf, event.Title, true)
|
||||
ps.Uint8(bf, event.Reward, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
ps.Uint8(bf, "", false)
|
||||
bf.WriteUint8(event.Unk6)
|
||||
ps.Uint8(bf, event.String0, true)
|
||||
ps.Uint8(bf, event.String1, true)
|
||||
ps.Uint8(bf, event.String2, true)
|
||||
ps.Uint8(bf, event.String3, true)
|
||||
ps.Uint8(bf, event.Link, true)
|
||||
for i := range event.Categories {
|
||||
campaignLinks = append(campaignLinks, CampaignLink{event.Categories[i], event.ID})
|
||||
}
|
||||
}
|
||||
|
||||
if len(events) > 255 {
|
||||
@@ -116,7 +98,7 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
for _, event := range events {
|
||||
bf.WriteUint32(event.ID)
|
||||
bf.WriteUint8(1) // Related to stamp count
|
||||
bf.WriteUint8(1) // Always 1?
|
||||
bf.WriteBytes([]byte(event.Prefix))
|
||||
}
|
||||
|
||||
@@ -153,156 +135,42 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfStateCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfStateCampaign)
|
||||
bf := byteframe.NewByteFrame()
|
||||
var required int
|
||||
var deadline time.Time
|
||||
var stamps []uint32
|
||||
|
||||
err := s.server.db.Select(&stamps, "SELECT id FROM campaign_state WHERE campaign_id = $1 AND character_id = $2", pkt.CampaignID, s.charID)
|
||||
if err != nil {
|
||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
err = s.server.db.QueryRow(`SELECT stamps, end_time FROM campaigns WHERE id = $1`, pkt.CampaignID).Scan(&required, &deadline)
|
||||
if err != nil {
|
||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
bf.WriteUint16(uint16(len(stamps) + 1))
|
||||
|
||||
if required == 0 {
|
||||
required = 1 // TODO: I don't understand how this is supposed to work
|
||||
}
|
||||
|
||||
if len(stamps) < required {
|
||||
bf.WriteUint16(0)
|
||||
} else if len(stamps) >= required || deadline.After(time.Now()) {
|
||||
bf.WriteUint16(2)
|
||||
}
|
||||
|
||||
for _, v := range stamps {
|
||||
bf.WriteUint32(v)
|
||||
}
|
||||
|
||||
bf.WriteUint16(1)
|
||||
bf.WriteUint16(0)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfApplyCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfApplyCampaign)
|
||||
|
||||
// Check if the code exists and check if it's a multi-code
|
||||
var multi bool
|
||||
err := s.server.db.QueryRow(`SELECT multi FROM public.campaign_codes WHERE code = $1 GROUP BY multi`, pkt.Code).Scan(&multi)
|
||||
if err != nil {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the code is already used
|
||||
var exists bool
|
||||
if multi {
|
||||
s.server.db.QueryRow(`SELECT COUNT(*) FROM public.campaign_state WHERE code = $1 AND character_id = $2`, pkt.Code, s.charID).Scan(&exists)
|
||||
} else {
|
||||
s.server.db.QueryRow(`SELECT COUNT(*) FROM public.campaign_state WHERE code = $1`, pkt.Code).Scan(&exists)
|
||||
}
|
||||
if exists {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
|
||||
s.server.db.Exec(`INSERT INTO public.campaign_state (code, campaign_id, character_id) VALUES ($1, $2, $3)`, pkt.Code, pkt.CampaignID, s.charID)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateItem)
|
||||
items := []struct {
|
||||
Unk0 uint32
|
||||
Unk1 uint16
|
||||
Unk2 uint16
|
||||
Unk3 uint16
|
||||
Unk4 uint32
|
||||
Unk5 uint32
|
||||
}{}
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
var stamps, required, rewardType uint16
|
||||
var deadline time.Time
|
||||
err := s.server.db.QueryRow(`SELECT COUNT(*) FROM campaign_state WHERE campaign_id = $1 AND character_id = $2`, pkt.CampaignID, s.charID).Scan(&stamps)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
err = s.server.db.QueryRow(`SELECT stamps, reward_type, end_time FROM campaigns WHERE id = $1`, pkt.CampaignID).Scan(&required, &rewardType, &deadline)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
|
||||
if required == 0 {
|
||||
required = 1 // TODO: I don't understand how this is supposed to work
|
||||
}
|
||||
|
||||
if stamps >= required {
|
||||
var items []CampaignReward
|
||||
if rewardType == 2 {
|
||||
var exists int
|
||||
s.server.db.QueryRow(`SELECT COUNT(*) FROM campaign_quest WHERE campaign_id = $1 AND character_id = $2`, pkt.CampaignID, s.charID).Scan(&exists)
|
||||
if exists > 0 {
|
||||
err = s.server.db.Select(&items, `
|
||||
SELECT id, item_type, quantity, item_id, TO_TIMESTAMP(0) AS deadline FROM campaign_rewards
|
||||
WHERE campaign_id = $1 AND item_type != 9
|
||||
AND NOT EXISTS (SELECT 1 FROM campaign_rewards_claimed WHERE reward_id = campaign_rewards.id AND character_id = $2)
|
||||
`, pkt.CampaignID, s.charID)
|
||||
} else {
|
||||
err = s.server.db.Select(&items, `
|
||||
SELECT cr.id, cr.item_type, cr.quantity, cr.item_id, COALESCE(c.end_time, TO_TIMESTAMP(0)) AS deadline FROM campaign_rewards cr
|
||||
JOIN campaigns c ON cr.campaign_id = c.id
|
||||
WHERE campaign_id = $1 AND item_type = 9`, pkt.CampaignID)
|
||||
}
|
||||
} else {
|
||||
err = s.server.db.Select(&items, `
|
||||
SELECT id, item_type, quantity, item_id, TO_TIMESTAMP(0) AS deadline FROM campaign_rewards
|
||||
WHERE campaign_id = $1
|
||||
AND NOT EXISTS (SELECT 1 FROM campaign_rewards_claimed WHERE reward_id = campaign_rewards.id AND character_id = $2)
|
||||
`, pkt.CampaignID, s.charID)
|
||||
}
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
|
||||
bf.WriteUint16(uint16(len(items)))
|
||||
for _, item := range items {
|
||||
bf.WriteUint32(item.ID)
|
||||
bf.WriteUint16(item.ItemType)
|
||||
bf.WriteUint16(item.Quantity)
|
||||
bf.WriteUint16(item.ItemID) //HACK:placed quest id in this field to fit with Item No pattern. however it could be another field... possibly the other unks.
|
||||
bf.WriteUint16(0) //Unk4, gets cast to uint8
|
||||
bf.WriteUint32(0) //Unk5
|
||||
bf.WriteUint32(uint32(deadline.Unix()))
|
||||
}
|
||||
if len(items) == 0 {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
bf.WriteUint16(uint16(len(items)))
|
||||
for _, item := range items {
|
||||
bf.WriteUint32(item.Unk0)
|
||||
bf.WriteUint16(item.Unk1)
|
||||
bf.WriteUint16(item.Unk2)
|
||||
bf.WriteUint16(item.Unk3)
|
||||
bf.WriteUint32(item.Unk4)
|
||||
bf.WriteUint32(item.Unk5)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfAcquireItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfAcquireItem)
|
||||
for _, id := range pkt.RewardIDs {
|
||||
s.server.db.Exec(`INSERT INTO campaign_rewards_claimed (reward_id, character_id) VALUES ($1, $2)`, id, s.charID)
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfTransferItem)
|
||||
if pkt.ItemType == 9 {
|
||||
var campaignID uint32
|
||||
err := s.server.db.QueryRow(`
|
||||
SELECT ce.campaign_id FROM campaign_rewards ce
|
||||
JOIN event_quests eq ON ce.item_id = eq.quest_id
|
||||
WHERE eq.id = $1
|
||||
`, pkt.QuestID).Scan(&campaignID)
|
||||
if err == nil {
|
||||
s.server.db.Exec(`INSERT INTO campaign_quest (campaign_id, character_id) VALUES ($1, $2)`, campaignID, s.charID)
|
||||
}
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
s.stage.Lock()
|
||||
newObj := &Object{
|
||||
id: s.NextObjectID(),
|
||||
id: s.getObjectId(),
|
||||
ownerCharID: s.charID,
|
||||
x: pkt.X,
|
||||
y: pkt.Y,
|
||||
|
||||
@@ -85,6 +85,10 @@ func BackportQuest(data []byte) []byte {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _config.ErupeConfig.RealClientMode <= _config.S6 {
|
||||
binary.LittleEndian.PutUint32(data[16:20], binary.LittleEndian.Uint32(data[8:12]))
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -277,33 +281,7 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
|
||||
}
|
||||
bf.WriteUint8(questType)
|
||||
if questType == 9 {
|
||||
var stamps, required int
|
||||
var deadline time.Time
|
||||
err := s.server.db.QueryRow(`SELECT COUNT(*) FROM campaign_state WHERE campaign_id = (
|
||||
SELECT campaign_id
|
||||
FROM campaign_rewards
|
||||
WHERE item_type = 9
|
||||
AND item_id = $1
|
||||
) AND character_id = $2`, questId, s.charID).Scan(&stamps)
|
||||
err2 := s.server.db.QueryRow(`SELECT stamps, end_time
|
||||
FROM campaigns
|
||||
WHERE id = (
|
||||
SELECT campaign_id
|
||||
FROM campaign_rewards
|
||||
WHERE item_type = 9
|
||||
AND item_id = $1
|
||||
)`, questId).Scan(&required, &deadline)
|
||||
|
||||
if required == 0 {
|
||||
required = 1 // TODO: I don't understand how this is supposed to work
|
||||
}
|
||||
|
||||
// Check if there are enough stamps to activate the quest, the deadline hasn't passed, and there are no errors
|
||||
if stamps >= required && deadline.After(time.Now()) && err == nil && err2 == nil {
|
||||
bf.WriteBool(true)
|
||||
} else {
|
||||
bf.WriteBool(false)
|
||||
}
|
||||
bf.WriteBool(false)
|
||||
} else {
|
||||
bf.WriteBool(true)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"erupe-ce/common/byteframe"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -64,28 +65,24 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
|
||||
// Confirm the stage entry.
|
||||
doAckSimpleSucceed(s, ackHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
|
||||
var temp mhfpacket.MHFPacket
|
||||
newNotif := byteframe.NewByteFrame()
|
||||
|
||||
// Cast existing user data to new user
|
||||
if !s.userEnteredStage {
|
||||
s.userEnteredStage = true
|
||||
if !s.loaded {
|
||||
s.loaded = true
|
||||
|
||||
for _, session := range s.server.sessions {
|
||||
if s == session {
|
||||
if s == session || !session.loaded {
|
||||
continue
|
||||
}
|
||||
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
|
||||
newNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(newNotif, s.clientContext)
|
||||
for i := 0; i < 3; i++ {
|
||||
temp = &mhfpacket.MsgSysNotifyUserBinary{
|
||||
CharID: session.charID,
|
||||
BinaryType: uint8(i + 1),
|
||||
}
|
||||
newNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(newNotif, s.clientContext)
|
||||
}
|
||||
session.QueueSendMHF(&mhfpacket.MsgSysInsertUser{CharID: s.charID})
|
||||
session.QueueSendMHF(&mhfpacket.MsgSysNotifyUserBinary{CharID: s.charID, BinaryType: 1})
|
||||
session.QueueSendMHF(&mhfpacket.MsgSysNotifyUserBinary{CharID: s.charID, BinaryType: 2})
|
||||
session.QueueSendMHF(&mhfpacket.MsgSysNotifyUserBinary{CharID: s.charID, BinaryType: 3})
|
||||
s.QueueSendMHF(&mhfpacket.MsgSysInsertUser{CharID: session.charID})
|
||||
s.QueueSendMHF(&mhfpacket.MsgSysNotifyUserBinary{CharID: session.charID, BinaryType: 1})
|
||||
s.QueueSendMHF(&mhfpacket.MsgSysNotifyUserBinary{CharID: session.charID, BinaryType: 2})
|
||||
s.QueueSendMHF(&mhfpacket.MsgSysNotifyUserBinary{CharID: session.charID, BinaryType: 3})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ type Server struct {
|
||||
acceptConns chan net.Conn
|
||||
deleteConns chan net.Conn
|
||||
sessions map[net.Conn]*Session
|
||||
objectIDs map[*Session]uint16
|
||||
listener net.Listener // Listener that is created when Server.Start is called.
|
||||
isShuttingDown bool
|
||||
|
||||
@@ -155,7 +154,6 @@ func NewServer(config *Config) *Server {
|
||||
acceptConns: make(chan net.Conn),
|
||||
deleteConns: make(chan net.Conn),
|
||||
sessions: make(map[net.Conn]*Session),
|
||||
objectIDs: make(map[*Session]uint16),
|
||||
stages: make(map[string]*Stage),
|
||||
userBinaryParts: make(map[userBinaryPartID][]byte),
|
||||
semaphore: make(map[string]*Semaphore),
|
||||
@@ -280,6 +278,20 @@ func (s *Server) manageSessions() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) getObjectId() uint16 {
|
||||
ids := make(map[uint16]struct{})
|
||||
for _, sess := range s.sessions {
|
||||
ids[sess.objectID] = struct{}{}
|
||||
}
|
||||
for i := uint16(1); i < 100; i++ {
|
||||
if _, ok := ids[i]; !ok {
|
||||
return i
|
||||
}
|
||||
}
|
||||
s.logger.Warn("object ids overflowed", zap.Int("sessions", len(s.sessions)))
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *Server) invalidateSessions() {
|
||||
for {
|
||||
if s.isShuttingDown {
|
||||
|
||||
@@ -36,8 +36,10 @@ type Session struct {
|
||||
clientContext *clientctx.ClientContext
|
||||
lastPacket time.Time
|
||||
|
||||
objectIndex uint16
|
||||
userEnteredStage bool // If the user has entered a stage before
|
||||
objectID uint16
|
||||
objectIndex uint16
|
||||
loaded bool
|
||||
|
||||
stage *Stage
|
||||
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
|
||||
stagePass string // Temporary storage
|
||||
@@ -83,12 +85,12 @@ func NewSession(server *Server, conn net.Conn) *Session {
|
||||
sendPackets: make(chan packet, 20),
|
||||
clientContext: &clientctx.ClientContext{}, // Unused
|
||||
lastPacket: time.Now(),
|
||||
objectID: server.getObjectId(),
|
||||
sessionStart: TimeAdjusted().Unix(),
|
||||
stageMoveStack: stringstack.New(),
|
||||
ackStart: make(map[uint32]time.Time),
|
||||
semaphoreID: make([]uint16, 2),
|
||||
}
|
||||
s.SetObjectID()
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -292,30 +294,9 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) SetObjectID() {
|
||||
for i := uint16(1); i < 127; i++ {
|
||||
exists := false
|
||||
for _, j := range s.server.objectIDs {
|
||||
if i == j {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
s.server.objectIDs[s] = i
|
||||
return
|
||||
}
|
||||
}
|
||||
s.server.objectIDs[s] = 0
|
||||
}
|
||||
|
||||
func (s *Session) NextObjectID() uint32 {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(s.server.objectIDs[s])
|
||||
func (s *Session) getObjectId() uint32 {
|
||||
s.objectIndex++
|
||||
bf.WriteUint16(s.objectIndex)
|
||||
bf.Seek(0, 0)
|
||||
return bf.ReadUint32()
|
||||
return uint32(s.objectID)<<16 | uint32(s.objectIndex)
|
||||
}
|
||||
|
||||
func (s *Session) GetSemaphoreID() uint32 {
|
||||
|
||||
Reference in New Issue
Block a user