diff --git a/AUTHORS.md b/AUTHORS.md index 618d34264..5617c5308 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,29 +1,30 @@ -# List of AUTHORS who contributed over time to the Erupe project +# List of authors who contributed to Erupe ## Point of current development The project is currently developed under https://github.com/ZeruLight/Erupe ## History of development Development of this project dates back to 2019, and was developed under various umbrellas over time: -* Cappuccino (Fist/Ando/Ellie42) (The Erupe Developers), 2019-2020 (https://github.com/Ellie42/Erupe / https://github.com/ricochhet/Erupe-Legacy) (Still active closed source) +* Cappuccino (Fist/Ando/Ellie42) ("The Erupe Developers"), 2019-2020 (https://github.com/Ellie42/Erupe / https://github.com/ricochhet/Erupe-Legacy) (Still active closed source) * Einherjar Team, ????-2022 Feb (There is no git history for this period, this team's work was taken and used as a foundation for future repositories) * Community Edition, 2022 (https://github.com/xl3lackout/Erupe) -* Zerulight, 2022-2023 (https://github.com/ZeruLight/Erupe) +* sekaiwish Fork, 2022 (https://github.com/sekaiwish/Erupe) +* ZeruLight, 2022-2023 (https://github.com/ZeruLight/Erupe) ## Authorship of the code Authorship is assigned for each commit within the git history, which is stored in these git repos: * https://github.com/ZeruLight/Erupe -* https://github.com/Ellie42/Erupe +* https://github.com/Ellie42/Erupe * https://github.com/ricochhet/Erupe-Legacy * https://github.com/xl3lackout/Erupe - -Note there is a divergence between Ellie42s branch and xl3lackout where history has been lost. -Unfortunately, we have no detailed information on the history of the Erupe pre-2022 -if somebody can provide information, please contact us, so that we can make this history available. +Note the divergence between Ellie42's branch and xl3lackout's where history has been lost. + +Unfortunately, we have no detailed information on the history of Erupe before 2022. +If somebody can provide information, please contact us, so that we can make this history available. ## Exceptions with third-party libraries The third-party libraries have their own way of addressing authorship and the authorship of commits importing/updating a third-party library reflects who did the importing instead of who wrote the code within the commit. -The Authors of third-party libraries are not explicitly mentioned, and usually is possible to obtain from the files belonging to the third-party libraries. \ No newline at end of file +The authors of third-party libraries are not explicitly mentioned, and usually is possible to obtain from the files belonging to the third-party libraries. \ No newline at end of file diff --git a/README.md b/README.md index b9be1fb05..8ac37b22b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Erupe -## Client Compatiblity +## Client Compatibility ### Platforms - PC - PlayStation 3 @@ -34,15 +34,15 @@ If you want to modify or compile Erupe yourself, please read on. ## Docker -Please see the readme in [docker/README.md](./docker/README.md). At the moment this is only really good for quick installs and checking out development not for production. +Please see [docker/README.md](./docker/README.md). This is intended for quick installs and development, not for production. ## Schemas We source control the following schemas: -- Initialisation Schemas: These initialise the application database to a clean install from a specific version. -- Update Schemas: These are update files they should be ran in order of version to get to the latest schema. -- Patch Schemas: These are for development and should be ran from the lastest available update schema or initial schema. These eventually get condensed into `Update Schemas` and then deleted when updated to a new version. -- Bundled Schemas: These are demo reference files to allow servers to be able to roll their own shops, distributions gachas and scenarios set ups. +- Initialization Schema: This initializes the application database to a specific version (9.1.0). +- Update Schemas: These are update files that should be ran on top of the initialization schema. +- Patch Schemas: These are for development and should be run after running all initialization and update schema. These get condensed into `Update Schemas` and deleted when updated to a new release. +- Bundled Schemas: These are demo reference files to give servers standard set-ups. Note: Patch schemas are subject to change! You should only be using them if you are following along with development. diff --git a/network/mhfpacket/msg_mhf_update_guacot.go b/network/mhfpacket/msg_mhf_update_guacot.go index 729c84547..2afcbad5c 100644 --- a/network/mhfpacket/msg_mhf_update_guacot.go +++ b/network/mhfpacket/msg_mhf_update_guacot.go @@ -31,8 +31,8 @@ func (m *MsgMhfUpdateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien m.AckHandle = bf.ReadUint32() m.EntryCount = bf.ReadUint16() bf.ReadUint16() // Zeroed - var temp Goocoo for i := 0; i < int(m.EntryCount); i++ { + var temp Goocoo temp.Index = bf.ReadUint32() for j := 0; j < 22; j++ { temp.Data1 = append(temp.Data1, bf.ReadInt16()) diff --git a/schemas/initialisation-schema/9.1-init.sql b/schemas/init.sql similarity index 100% rename from schemas/initialisation-schema/9.1-init.sql rename to schemas/init.sql diff --git a/schemas/patch-schema/22-clan-changing-room.sql b/schemas/patch-schema/22-clan-changing-room.sql new file mode 100644 index 000000000..4af9ef18a --- /dev/null +++ b/schemas/patch-schema/22-clan-changing-room.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE guilds ADD COLUMN IF NOT EXISTS room_rp INT DEFAULT 0; +ALTER TABLE guilds ADD COLUMN IF NOT EXISTS room_expiry TIMESTAMP WITHOUT TIME ZONE; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 5613bd53b..096419137 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -51,6 +51,8 @@ type Guild struct { MemberCount uint16 `db:"member_count"` RankRP uint32 `db:"rank_rp"` EventRP uint32 `db:"event_rp"` + RoomRP uint16 `db:"room_rp"` + RoomExpiry time.Time `db:"room_expiry"` Comment string `db:"comment"` PugiName1 string `db:"pugi_name_1"` PugiName2 string `db:"pugi_name_2"` @@ -153,6 +155,8 @@ SELECT g.name, rank_rp, event_rp, + room_rp, + COALESCE(room_expiry, '1970-01-01') AS room_expiry, main_motto, sub_motto, created_at, @@ -706,7 +710,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(uint32(response)) case mhfpacket.OperateGuildDonateRank: - bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false)) + bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, 0)) case mhfpacket.OperateGuildSetApplicationDeny: s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID) case mhfpacket.OperateGuildSetApplicationAllow: @@ -747,10 +751,11 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { // TODO: This doesn't implement blocking, if someone unlocked the same outfit at the same time s.server.db.Exec(`UPDATE guilds SET pugi_outfits=pugi_outfits+$1 WHERE id=$2`, int(math.Pow(float64(pkt.Data1.ReadUint32()), 2)), guild.ID) case mhfpacket.OperateGuildDonateRoom: - // TODO: Where does this go? + quantity := uint16(pkt.Data1.ReadUint32()) + bf.WriteBytes(handleDonateRP(s, quantity, guild, 2)) case mhfpacket.OperateGuildDonateEvent: quantity := uint16(pkt.Data1.ReadUint32()) - bf.WriteBytes(handleDonateRP(s, quantity, guild, true)) + bf.WriteBytes(handleDonateRP(s, quantity, guild, 1)) // TODO: Move this value onto rp_yesterday and reset to 0... daily? s.server.db.Exec(`UPDATE guild_characters SET rp_today=rp_today+$1 WHERE character_id=$2`, quantity, s.charID) case mhfpacket.OperateGuildEventExchange: @@ -794,20 +799,37 @@ func handleChangePugi(s *Session, outfit uint8, guild *Guild, num int) { guild.Save(s) } -func handleDonateRP(s *Session, amount uint16, guild *Guild, isEvent bool) []byte { +func handleDonateRP(s *Session, amount uint16, guild *Guild, _type int) []byte { bf := byteframe.NewByteFrame() bf.WriteUint32(0) saveData, err := GetCharacterSaveData(s, s.charID) if err != nil { return bf.Data() } + var resetRoom bool + if _type == 2 { + var currentRP uint16 + s.server.db.QueryRow(`SELECT room_rp FROM guilds WHERE id = $1`, guild.ID).Scan(¤tRP) + if currentRP+amount >= 30 { + amount = 30 - currentRP + resetRoom = true + } + } saveData.RP -= amount saveData.Save(s) - updateSQL := "UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2" - if isEvent { - updateSQL = "UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2" + switch _type { + case 0: + s.server.db.Exec(`UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2`, amount, guild.ID) + case 1: + s.server.db.Exec(`UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2`, amount, guild.ID) + case 2: + if resetRoom { + s.server.db.Exec(`UPDATE guilds SET room_rp = 0 WHERE id = $1`, guild.ID) + s.server.db.Exec(`UPDATE guilds SET room_expiry = $1 WHERE id = $2`, TimeAdjusted().Add(time.Hour*24*7), guild.ID) + } else { + s.server.db.Exec(`UPDATE guilds SET room_rp = room_rp + $1 WHERE id = $2`, amount, guild.ID) + } } - s.server.db.Exec(updateSQL, amount, guild.ID) bf.Seek(0, 0) bf.WriteUint32(uint32(saveData.RP)) return bf.Data() @@ -1001,8 +1023,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint8(limit) bf.WriteUint32(55000) - bf.WriteUint32(0) - bf.WriteUint16(0) // Changing Room RP + bf.WriteUint32(uint32(guild.RoomExpiry.Unix())) + bf.WriteUint16(guild.RoomRP) bf.WriteUint16(0) // Ignored if guild.AllianceID > 0 { diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index bc0b3689e..e3196bc44 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -148,9 +148,21 @@ func removeSessionFromStage(s *Session) { destructEmptySemaphores(s) } +func isStageFull(s *Session, StageID string) bool { + if stage, exists := s.server.stages[StageID]; exists { + return len(stage.reservedClientSlots)+len(stage.clients) >= int(stage.maxPlayers) + } + return false +} + func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnterStage) + if isStageFull(s, pkt.StageID) { + doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + return + } + // Push our current stage ID to the movement stack before entering another one. if s.stage != nil { s.stage.Lock() @@ -175,6 +187,12 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { backStage = "sl1Ns200p0a0u0" } + if isStageFull(s, backStage) { + s.stageMoveStack.Push(backStage) + doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + return + } + if _, exists := s.stage.reservedClientSlots[s.charID]; exists { delete(s.stage.reservedClientSlots, s.charID) } @@ -188,6 +206,12 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysMoveStage) + + if isStageFull(s, pkt.StageID) { + doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + return + } + doStageTransfer(s, pkt.AckHandle, pkt.StageID) } diff --git a/server/channelserver/sys_stage.go b/server/channelserver/sys_stage.go index dbfcbb7c3..b0f94a09a 100644 --- a/server/channelserver/sys_stage.go +++ b/server/channelserver/sys_stage.go @@ -59,7 +59,7 @@ func NewStage(ID string) *Stage { objects: make(map[uint32]*Object), objectIndex: 0, rawBinaryData: make(map[stageBinaryKey][]byte), - maxPlayers: 4, + maxPlayers: 127, } return s } diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 2f491e8c0..452b02475 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -38,25 +38,24 @@ func (s *Session) makeSignResponse(uid uint32) []byte { return bf.Data() } - bf.WriteUint8(uint8(SIGN_SUCCESS)) // resp_code - if (s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "") || s.client == PS3 { - bf.WriteUint8(2) - } else { - bf.WriteUint8(0) + if s.client == PS3 && (s.server.erupeConfig.PatchServerFile == "" || s.server.erupeConfig.PatchServerManifest == "") { + bf.WriteUint8(uint8(SIGN_EABORT)) + return bf.Data() } + + bf.WriteUint8(uint8(SIGN_SUCCESS)) + bf.WriteUint8(2) // patch server count bf.WriteUint8(1) // entrance server count bf.WriteUint8(uint8(len(chars))) bf.WriteUint32(tokenID) bf.WriteBytes([]byte(sessToken)) bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix())) if s.client == PS3 { - ps.Uint8(bf, fmt.Sprintf(`ps3-%s.zerulight.cc`, s.server.erupeConfig.Language), false) - ps.Uint8(bf, fmt.Sprintf(`ps3-%s.zerulight.cc`, s.server.erupeConfig.Language), false) + ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerManifest), false) + ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerFile), false) } else { - if s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "" { - ps.Uint8(bf, s.server.erupeConfig.PatchServerManifest, false) - ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false) - } + ps.Uint8(bf, s.server.erupeConfig.PatchServerManifest, false) + ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false) } if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" { ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", s.server.erupeConfig.Entrance.Port), false)