@@ -9,6 +9,7 @@ import androidx.compose.runtime.setValue
99import androidx.compose.ui.util.fastCoerceAtLeast
1010import androidx.compose.ui.util.fastCoerceAtMost
1111import androidx.compose.ui.util.fastCoerceIn
12+ import androidx.compose.ui.util.fastMapTo
1213import androidx.lifecycle.viewModelScope
1314import com.imsproject.common.gameserver.GameAction
1415import com.imsproject.common.gameserver.GameType
@@ -67,7 +68,8 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
6768 val myArc = Arc ()
6869 val opponentArc = Arc ()
6970 private lateinit var myFrequencyTracker : FrequencyTracker
70- private lateinit var opponentFrequencyTracker : FrequencyTracker
71+ @Volatile
72+ private var opponentFrequency = 0f
7173
7274 private var _released = MutableStateFlow (true )
7375 val released : StateFlow <Boolean > = _released
@@ -96,17 +98,23 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
9698 override fun onCreate (intent : Intent , context : Context ) {
9799 super .onCreate(intent,context)
98100
101+ setupWavPlayer(context)
102+ myFrequencyTracker = FrequencyTracker ()
103+
99104 if (ACTIVITY_DEBUG_MODE ){
100105 viewModelScope.launch(Dispatchers .Default ) {
106+ val opponentFrequencyTracker = FrequencyTracker ()
101107 while (true ) {
102- var angle = 0.0f
103- while (angle < 360 * 15 ){
104- opponentArc.startAngle = angle
105- opponentFrequencyTracker.addSample(angle)
106- angle + = 4
108+ var rawAngle = 0.0f
109+ while (rawAngle < 360 * 15 ){
110+ opponentFrequencyTracker.addSample(rawAngle)
111+ opponentFrequency = opponentFrequencyTracker.frequency
112+ updateArc(rawAngle,opponentArc)
113+ rawAngle + = 4
107114 delay(16 )
108115 }
109116 _opponentReleased .value = true
117+ opponentFrequency = 0f
110118 opponentFrequencyTracker.reset()
111119 delay(2000 )
112120 _opponentReleased .value = false
@@ -115,8 +123,6 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
115123 return
116124 }
117125
118- setupWavPlayer(context)
119-
120126 // set up sync params
121127 val syncTolerance = intent.getLongExtra(" $PACKAGE_PREFIX .syncTolerance" , - 1 )
122128 if (syncTolerance <= 0L ) {
@@ -133,19 +139,13 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
133139 Log .d(TAG , " syncTolerance: $syncTolerance " )
134140 Log .d(TAG , " syncWindowLength: $syncWindowLength " )
135141
136- // set up frequency trackers
137- myFrequencyTracker = FrequencyTracker ()
138- opponentFrequencyTracker = FrequencyTracker ()
139-
140142 // start the frequency tracking loop
141143 viewModelScope.launch(Dispatchers .Default ){
142144 while (true ){
143145 delay(100 ) // run this loop roughly 10 times per second
144- if (inSync){
145- val timestamp = getCurrentGameTime()
146- addEvent(SessionEvent .frequency(playerId, timestamp, myFrequencyTracker.frequency.toString()))
147- addEvent(SessionEvent .opponentFrequency(playerId, timestamp, opponentFrequencyTracker.frequency.toString()))
148- }
146+ val timestamp = getCurrentGameTime()
147+ addEvent(SessionEvent .frequency(playerId, timestamp, myFrequencyTracker.frequency.toString()))
148+ addEvent(SessionEvent .opponentFrequency(playerId,timestamp,opponentFrequency.toString()))
149149 }
150150 }
151151 }
@@ -159,9 +159,9 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
159159 }
160160
161161 if (inBounds){
162- updateMyArc (rawAngle)
162+ updateArc (rawAngle,myArc )
163163 _released .value = false
164- myFrequencyTracker.addSample(myArc.startAngle )
164+ myFrequencyTracker.addSample(rawAngle )
165165 } else {
166166 _released .value = true
167167 myFrequencyTracker.reset()
@@ -172,15 +172,17 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
172172 // send input to server
173173 viewModelScope.launch(Dispatchers .IO ) {
174174 val timestamp = getCurrentGameTime()
175- val data = if (inBounds) myArc.startAngle.toString() else UNDEFINED_ANGLE .toString()
175+ val angle = if (inBounds) rawAngle else UNDEFINED_ANGLE
176+ val frequency = myFrequencyTracker.frequency
177+ val data = " $angle ,$frequency "
176178 model.sendUserInput(timestamp, packetTracker.newPacket(),data)
177179 addEvent(SessionEvent .angle(playerId,timestamp,data))
178180 }
179181 }
180182
181183 fun inSync () = (
182184 ! released.value && ! opponentReleased.value
183- && (myFrequencyTracker.frequency - opponentFrequencyTracker.frequency )
185+ && (myFrequencyTracker.frequency - opponentFrequency )
184186 .absoluteValue < WINE_GLASSES_SYNC_FREQUENCY_THRESHOLD
185187 ).also { inSync = it }
186188
@@ -203,7 +205,7 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
203205 Log .e(TAG , " handleGameAction: missing timestamp in user input action" )
204206 return
205207 }
206- val angle = action.data?.toFloat() ? : run {
208+ val data = action.data? : run {
207209 Log .e(TAG , " handleGameAction: missing data in user input action" )
208210 return
209211 }
@@ -214,44 +216,49 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
214216
215217 val arrivedTimestamp = getCurrentGameTime()
216218
217- if (angle == UNDEFINED_ANGLE ){
219+ val outOfOrder = packetTracker.receivedOtherPacket(sequenceNumber)
220+ if (outOfOrder) return
221+
222+ val (rawAngle, frequency) = data.split(" ," ).let {
223+ Pair (it[0 ].toFloat(), it[1 ].toFloat())
224+ }
225+ if (rawAngle == UNDEFINED_ANGLE ){
218226 _opponentReleased .value = true
219- opponentFrequencyTracker.reset()
227+ opponentFrequency = 0f
220228 } else {
221229 _opponentReleased .value = false
222- opponentArc.startAngle = angle
223- opponentFrequencyTracker.addSample(angle )
230+ opponentFrequency = frequency
231+ updateArc(rawAngle,opponentArc )
224232 }
225233
226- packetTracker.receivedOtherPacket(sequenceNumber)
227- addEvent(SessionEvent .opponentAngle(playerId,arrivedTimestamp,angle.toString()))
234+ addEvent(SessionEvent .opponentAngle(playerId,arrivedTimestamp,rawAngle.toString()))
228235 }
229236 else -> super .handleGameAction(action)
230237 }
231238 }
232239
233- private fun updateMyArc (angle : Float ){
240+ private fun updateArc (angle : Float , arc : Arc ){
234241
235242 // =========== for current iteration =============== |
236243
237244 // calculate the skew angle to show the arc ahead of the finger
238245 // based on the calculations of the previous iteration
239- val angleSkew = myArc .angleSkew
240- myArc .startAngle = addToAngle(angle,
241- myArc .direction.fastCoerceIn(- DIRECTION_MAX_OFFSET , DIRECTION_MAX_OFFSET ) * angleSkew
246+ val angleSkew = arc .angleSkew
247+ arc .startAngle = addToAngle(angle,
248+ arc .direction.fastCoerceIn(- DIRECTION_MAX_OFFSET , DIRECTION_MAX_OFFSET ) * angleSkew
242249 - MY_SWEEP_ANGLE / 2
243250 )
244251
245252 // ============== for next iteration =============== |
246253
247254 // prepare the skew angle for the next iteration
248- val previousAngle = myArc .previousAngle
255+ val previousAngle = arc .previousAngle
249256 var angleDiff = 0f
250257 if (previousAngle != UNDEFINED_ANGLE ){
251258 angleDiff = calculateAngleDiff(previousAngle, angle)
252- val previousAngleDiff = myArc .previousAngleDiff
259+ val previousAngleDiff = arc .previousAngleDiff
253260 val angleDiffDiff = angleDiff - previousAngleDiff
254- myArc .angleSkew = if (angleDiffDiff > 1 && angleDiff > 3 ){
261+ arc .angleSkew = if (angleDiffDiff > 1 && angleDiff > 3 ){
255262 (angleSkew + angleDiff * 0.75f ).fastCoerceAtMost(MAX_ANGLE_SKEW )
256263 } else if (angleDiffDiff < 1 ){
257264 (angleSkew - angleDiff * 0.375f ).fastCoerceAtLeast(MIN_ANGLE_SKEW )
@@ -264,22 +271,22 @@ class WineGlassesViewModel : GameViewModel(GameType.WINE_GLASSES) {
264271 // we add a bit to the max offset to prevent random jitter in the direction
265272 // we clamp the direction to the max offset when calculating the skewed angle
266273 if (previousAngle != UNDEFINED_ANGLE ){
267- val direction = myArc .direction
268- myArc .direction = if (isClockwise(previousAngle, angle)){
274+ val direction = arc .direction
275+ arc .direction = if (isClockwise(previousAngle, angle)){
269276 (direction + angleDiff * 0.2f ).fastCoerceAtMost(DIRECTION_MAX_OFFSET + 0.5f )
270277 } else if (! isClockwise(previousAngle, angle)){
271278 (direction - angleDiff * 0.2f ).fastCoerceAtLeast(- (DIRECTION_MAX_OFFSET + 0.5f ))
272279 } else {
273280 direction
274281 }
275- if (myArc .direction.isBetweenInclusive(- WINE_GLASSES_SYNC_FREQUENCY_THRESHOLD ,
282+ if (arc .direction.isBetweenInclusive(- WINE_GLASSES_SYNC_FREQUENCY_THRESHOLD ,
276283 WINE_GLASSES_SYNC_FREQUENCY_THRESHOLD
277- )) myArc .angleSkew = MIN_ANGLE_SKEW
284+ )) arc .angleSkew = MIN_ANGLE_SKEW
278285 }
279286
280287 // current angle becomes previous angle for the next iteration
281- myArc .previousAngle = angle
282- myArc .previousAngleDiff = angleDiff
288+ arc .previousAngle = angle
289+ arc .previousAngleDiff = angleDiff
283290 }
284291
285292 private fun setupWavPlayer (context : Context ){
0 commit comments