From 8aa5cbb72e6ad289582ac8fe687a7bfcc5e49beb Mon Sep 17 00:00:00 2001 From: Kyren223 Date: Fri, 23 May 2025 10:30:24 +0300 Subject: Fixed packet loss bug due to client not waiting before exiting for the writes to fully proccess --- internal/client/ui/core/state/state.go | 106 ++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 28 deletions(-) (limited to 'internal/client/ui/core/state/state.go') diff --git a/internal/client/ui/core/state/state.go b/internal/client/ui/core/state/state.go index 75fd8a7..1eafaee 100644 --- a/internal/client/ui/core/state/state.go +++ b/internal/client/ui/core/state/state.go @@ -35,23 +35,25 @@ type state struct { BlockedUsers map[snowflake.ID]struct{} // key is user id BlockingUsers map[snowflake.ID]struct{} // key is user id - LastReadMessages map[snowflake.ID]*snowflake.ID // key is frequency id or receiver id - Notifications map[snowflake.ID]int // key is frequency id or receiver id + LastReadMessages map[snowflake.ID]*snowflake.ID // key is frequency id or receiver id + RemoteNotifications map[snowflake.ID]int // key is frequency id or receiver id + LocalNotifications map[snowflake.ID]int // key is frequency id or receiver id } var State state = state{ - ChatState: map[snowflake.ID]ChatState{}, - LastFrequency: map[snowflake.ID]snowflake.ID{}, - Messages: map[snowflake.ID]*btree.BTreeG[data.Message]{}, - Networks: map[snowflake.ID]data.Network{}, - Frequencies: map[snowflake.ID][]data.Frequency{}, - Members: map[snowflake.ID]map[snowflake.ID]data.Member{}, - Users: map[snowflake.ID]data.User{}, - TrustedUsers: map[snowflake.ID]ed25519.PublicKey{}, - BlockedUsers: map[snowflake.ID]struct{}{}, - BlockingUsers: map[snowflake.ID]struct{}{}, - LastReadMessages: map[snowflake.ID]*snowflake.ID{}, - Notifications: map[snowflake.ID]int{}, + ChatState: map[snowflake.ID]ChatState{}, + LastFrequency: map[snowflake.ID]snowflake.ID{}, + Messages: map[snowflake.ID]*btree.BTreeG[data.Message]{}, + Networks: map[snowflake.ID]data.Network{}, + Frequencies: map[snowflake.ID][]data.Frequency{}, + Members: map[snowflake.ID]map[snowflake.ID]data.Member{}, + Users: map[snowflake.ID]data.User{}, + TrustedUsers: map[snowflake.ID]ed25519.PublicKey{}, + BlockedUsers: map[snowflake.ID]struct{}{}, + BlockingUsers: map[snowflake.ID]struct{}{}, + LastReadMessages: map[snowflake.ID]*snowflake.ID{}, + RemoteNotifications: map[snowflake.ID]int{}, + LocalNotifications: map[snowflake.ID]int{}, } type UserData struct { @@ -292,15 +294,13 @@ func UpdateNotifications(info *packet.NotificationsInfo) []snowflake.ID { ping := info.Pings[i] if ping != nil { - // log.Println(source, *ping) - State.Notifications[source] = int(*ping) + State.RemoteNotifications[source] = int(*ping) + if !IsFrequency(source) && !slices.Contains(Data.Signals, source) { signals = append(signals, source) - // log.Println("Signals:", Data.Signals) } } else { - // log.Println(source, "deleted") - delete(State.Notifications, info.Source[i]) + delete(State.RemoteNotifications, info.Source[i]) } } @@ -309,10 +309,10 @@ func UpdateNotifications(info *packet.NotificationsInfo) []snowflake.ID { func SendFinalData() { data := JsonUserData() - ch1 := gateway.SendAsync(&packet.SetUserData{ - Data: &data, - User: nil, - }) + // ch1 := gateway.SendAsync(&packet.SetUserData{ + // Data: &data, + // User: nil, + // }) sources := []snowflake.ID{} @@ -332,6 +332,10 @@ func SendFinalData() { } } + ch1 := gateway.SendAsync(&packet.SetUserData{ + Data: &data, + User: nil, + }) ch2 := gateway.SendAsync(&packet.SetLastReadMessages{ Source: sources, LastRead: lastReads, @@ -340,15 +344,55 @@ func SendFinalData() { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - select { - case <-ctx.Done(): - case <-ch1: - } + done := make(chan struct{}) + go func() { + <-ch1 + <-ch2 + close(done) + }() select { case <-ctx.Done(): - case <-ch2: + log.Println("Timeout while waiting for final writes to complete") + case <-done: + log.Println("All final writes completed successfully") } + + // HACK: Give a small grace period for the writes to be processed + // Tweak this value as needed + time.Sleep(20 * time.Millisecond) + + // TODO: + // I think the issue is that it's random which of these 2 requests goes + // first (bcz it only does the first request after the client disconnects) + // I could fix it server side but eh maybe not + // this should instead use a method that blocks until a response was + // received, which may need a new gateway method to do that as currently + // it just always sends to the prograg + // If this is tedious enough it might be worth it to just do it on the + // server side + + // Also later on I should probably remove the calculate notifs in core + // it can be replaced with just diff-ing incoming notifs + // this will most likely work fine although there are some issues with + // scopes like becoming an admin/no longer being admin or gaining + // or losing access to frequencies and of course msg deletions + // But it's probably the right approach (also WAYYYYYY faster) + + // Then there is also the issue of when switching to a frequency + // not yet receiving the history so it says "no keep" bcz it's not loaded + // but with history it would've said "yes keep" so the solutin would + // be to rework it quite a bit to make it stateless or smthing + // Then that should be most issues when it comes to notifications + // just need to make sure local/remote notifs reset properly when + // reaching the bottom + + // log.Println("BLOCKING...") + // <-ctx.Done() + // log.Println("CTX DANZO") + + // TODO: remove this before release + // assert.NoError(ctx.Err(), "context has ran out of time!") } func UpdateBlockedUsers(info *packet.BlockInfo) { @@ -375,3 +419,9 @@ func UpdateUsersInfo(info *packet.UsersInfo) { State.Users[user.ID] = user } } + +func MergedNotification(id snowflake.ID) (pings int, ok bool) { + remotePings, remoteOk := State.RemoteNotifications[id] + localPings, localOk := State.LocalNotifications[id] + return remotePings + localPings, remoteOk || localOk +} -- cgit v1.3.1