Skip to content

Commit

Permalink
fully fixed compressing replays where players or items get rollbacked…
Browse files Browse the repository at this point in the history
… into existence; removed test for never-increasing stocks since rollback and stock stealing can both interfere
  • Loading branch information
pcrain committed Feb 18, 2022
1 parent 56a8fe3 commit f1c65a8
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 51 deletions.
98 changes: 52 additions & 46 deletions src/compressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,26 +408,18 @@ namespace slip {

//XOR all of the remaining data for the item
uint16_t itype;
// TODO: not sure if I even need this split
if (ENCODE_VERSION_MIN(2)) {
//DOCUMENT
// we use ITEM_TYPE for other purposes
// we use ITEM_TYPE for other purposes, so don't XOR it
xorEncodeRange(O_ITEM_STATE,O_ITEM_XVEL,_x_item[slot]);
itype = readBE2U(&main_buf[_bp+O_ITEM_TYPE]) & 0x3FFF;
//DOCUMENT
// Ignore the bits used for storing defer information
itype = decodeDeferInfo(readBE2U(&main_buf[_bp+O_ITEM_TYPE]));
} else {
xorEncodeRange(O_ITEM_TYPE,O_ITEM_XVEL,_x_item[slot]);
itype = readBE2U(&main_buf[_bp+O_ITEM_TYPE]);
}
xorEncodeRange(O_ITEM_DAMAGE,O_ITEM_EXPIRE,_x_item[slot]);


// if(_debug && (!_encode_ver)) {
// std::streamsize ss = std::cout.precision();
// std::cout << std::setprecision(30);
// std::cout << "Item 0x" << std::hex << itype << std::dec << " in slot " << +slot << " ypos is " << readBE4F(&_rb[_bp+O_ITEM_YPOS]) << std::endl;
// std::cout << std::setprecision(ss);
// }

//Predict item positions based on velocity
predictVelocItem(slot,O_ITEM_XPOS);

Expand Down Expand Up @@ -826,14 +818,10 @@ namespace slip {
char* dupe_frames = new char[RB_SIZE]{0};
char* defer_pre[8];
char* defer_post[8];
char* defer_item[MAX_ITEMS_C];
for (unsigned i = 0; i < 8; ++i) {
defer_pre[i] = new char[RB_SIZE]{0};
defer_post[i] = new char[RB_SIZE]{0};
}
for (unsigned i = 0; i < MAX_ITEMS_C; ++i) {
defer_item[i] = new char[RB_SIZE]{0};
}
int max_frame = -125;
unsigned max_item_seen = 0;
int modframe = 0;
Expand Down Expand Up @@ -861,17 +849,16 @@ namespace slip {

// DOCUMENT
if(!unshuffle) {
// Determine the number of times each recent frame has been duplicated
modframe = ((cur_frame+256)%RB_SIZE);
if (cur_frame > max_frame) {
max_frame = cur_frame;
dupe_frames[modframe] = 0;
// reset the defer counters for each player
for (unsigned i = 0; i < 8; ++i) {
defer_pre[i][modframe] = 0;
defer_post[i][modframe] = 0;
}
for (unsigned i = 0; i < max_item_seen; ++i) {
defer_item[i][modframe] = 0;
}
} else {
dupe_frames[modframe] += 1;
}
Expand Down Expand Up @@ -911,16 +898,19 @@ namespace slip {
case Event::PRE_FRAME: //Includes follower
pid = uint8_t(main_buf[b+O_PLAYER])+4*uint8_t(main_buf[b+O_FOLLOWER]);
oid = 1+pid;
// DOCUMENT
if(!unshuffle) {
// check if we got rollback'd into existence
defer = (dupe_frames[modframe] - defer_pre[pid][modframe]);
if (defer > 0) {
std::cout << " deferred pre " << (1+pid) << " by " << +defer << " at byte " << b << std::endl;
// encode the number of frames we need to defer writing
// this rollback'd preframe into the stream when decoding
unsigned asid = readBE2U(&main_buf[b+O_ACTION_PRE]);
asid = encodeDeferInfo(asid,defer);
writeBE2U(asid,&main_buf[b+O_ACTION_PRE]);
}
// std::cout << " set defer to " << +(dupe_frames[modframe]) << " - " << +(defer_pre[pid][modframe]) << "+ 1 = " << +(defer_pre[pid][modframe]) << std::endl;
defer_pre[pid][modframe] = dupe_frames[modframe]+1;
}
// DOCUMENT
break;
case Event::ITEM_UPDATE:
oid = 9;
Expand All @@ -932,24 +922,17 @@ namespace slip {
if (MIN_VERSION(3,7,0)) {
ff = lookAheadToFinalizedFrame(&_rb[b]);

// DOCUMENT
// if we've seen a new item, check if it was rollbacked into existence
if(item_id >= max_item_seen) {
max_item_seen = item_id+1;
defer = dupe_frames[modframe];
if (defer > 0) {
if (dupe_frames[modframe] > 0) {
// encode the number of frames we need to defer writing
// this rollback'd item into the stream when decoding
unsigned item_type = readBE2U(&main_buf[b+O_ITEM_TYPE]);
std::cout << " deferred item " << (item_id)
<< " by " << +defer
<< " at byte " << b
<< " and frame " << (frame_counter[start_fp-1]%256)
<< " and final " << (ff%256)
<< " with rb " << finalized_counter[end_fp-1] << std::endl;
item_type |= (defer << 14);
item_type = encodeDeferInfo(item_type,dupe_frames[modframe]);
writeBE2U(item_type,&main_buf[b+O_ITEM_TYPE]);
}
}
defer_item[item_id][modframe] = dupe_frames[modframe]+1;
// DOCUMENT

item_id = encodeFrameIntoItemId(item_id,ff);
unsigned dec_id = encodeFrameIntoItemId(item_id,ff);
Expand Down Expand Up @@ -983,17 +966,19 @@ namespace slip {
case Event::POST_FRAME: //Includes follower
pid = uint8_t(main_buf[b+O_PLAYER])+4*uint8_t(main_buf[b+O_FOLLOWER]);
oid = 10+pid;
// DOCUMENT
if(!unshuffle) {
// check if we got rollback'd into existence
defer = (dupe_frames[modframe] - defer_post[pid][modframe]);
if (defer > 0) {
std::cout << " deferred post " << (1+pid) << " by " << +defer << " at byte " << b << std::endl;
// encode the number of frames we need to defer writing
// this rollback'd preframe into the stream when decoding
unsigned asid = readBE2U(&main_buf[b+O_ACTION_POST]);
asid = encodeDeferInfo(asid,defer);
writeBE2U(asid,&main_buf[b+O_ACTION_POST]);
}
// std::cout << " set defer to " << +(dupe_frames[modframe]) << " - " << +(defer_pre[pid][modframe]) << "+ 1 = " << +(defer_pre[pid][modframe]) << std::endl;
defer_post[pid][modframe] = dupe_frames[modframe]+1;
}
// DOCUMENT
break;
break;
case Event::BOOKEND:
oid = 18;
Expand Down Expand Up @@ -1104,15 +1089,14 @@ namespace slip {
lastshuffleframe = fnum;
dec_frames[frame_ptr] = fnum;

// DOCUMENT
// Determine the number of times each recent frame has been duplicated
modframe = ((fnum+256)%RB_SIZE);
if (fnum > max_frame) {
max_frame = fnum;
dupe_frames[modframe] = 0;
} else {
dupe_frames[modframe] += 1;
}
// DOCUMENT

// std::cout << "Decoded frame at " << frame_ptr << " as " << fnum << std::endl;
DOUT3(" " << (b) << " bytes read, " << (_game_loop_end-b) << " bytes left at frame " << fnum);
Expand Down Expand Up @@ -1140,6 +1124,17 @@ namespace slip {
// std::cout << fnum << " NO MATCH pre-frame " << i+1 << " @ " << decodeFrame(readBE4S(&ev_buf[1+i][cpos[1+i]+O_FRAME]), lastshuffleframe) << std::endl;
continue;
}
// Verify we don't need to defer writing this preframe event
// to the output stream due to rollback shenanigans
unsigned asid = readBE2U(&ev_buf[i][cpos[i]+O_ACTION_PRE]);
unsigned defer = getDeferInfo(asid);
if (defer > 0) {
if ((int)defer != dupe_frames[modframe]) {
continue;
}
// zero out the first two bits of action state id holding defer information
writeBE2U(decodeDeferInfo(asid),&ev_buf[i][cpos[i]+O_ACTION_PRE]);
}
// Copy the pre frame event over to the main buffer
DOUNSHUFFLE(i,Event::PRE_FRAME);
lastshufflepreframe[p] = fnum;
Expand Down Expand Up @@ -1191,17 +1186,17 @@ namespace slip {
// Verify we aren't repeating items this frame
if (int(item_id) <= last_id) { break; }

// DOCUMENT
// Verify we don't need to defer writing this item event
// to the output stream due to rollback shenanigans
unsigned item_type = readBE2U(&ev_buf[9][cpos[9]+O_ITEM_TYPE]);
int defer = (item_type >> 14);
unsigned defer = getDeferInfo(item_type);
if (defer > 0) {
if (defer != dupe_frames[modframe]) {
std::cout << "defer " << +defer << " != " << +dupe_frames[modframe] << std::endl;
if ((int)defer != dupe_frames[modframe]) {
break;
}
writeBE2U(item_type&0x3FFF,&ev_buf[9][cpos[9]+O_ITEM_TYPE]);
// zero out the first two bits of item type holding defer information
writeBE2U(decodeDeferInfo(item_type),&ev_buf[9][cpos[9]+O_ITEM_TYPE]);
}
// DOCUMENT

// Restore the actual item ID to the buffer
writeBE4U(item_id,&ev_buf[9][cpos[9]+O_ITEM_ID]);
Expand All @@ -1228,6 +1223,17 @@ namespace slip {
if (!DECODEFRAMEMATCHES(i,lastshufflepostframe[p])) {
continue;
}
// Verify we don't need to defer writing this postframe event
// to the output stream due to rollback shenanigans
unsigned asid = readBE2U(&ev_buf[i][cpos[i]+O_ACTION_POST]);
unsigned defer = getDeferInfo(asid);
if (defer > 0) {
if ((int)defer != dupe_frames[modframe]) {
continue;
}
// zero out the first two bits of action state id holding defer information
writeBE2U(decodeDeferInfo(asid),&ev_buf[i][cpos[i]+O_ACTION_POST]);
}
// Copy the post frame event over to the main buffer
DOUNSHUFFLE(i,Event::POST_FRAME);
lastshufflepostframe[p] = fnum;
Expand Down
13 changes: 13 additions & 0 deletions src/compressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const std::string COMPRESSOR_VERSION = "0.8.0"; //External version of this c
const uint32_t RAW_RNG_MASK = 0x40000000; //Second bit of unsigned int
const uint32_t MAGIC_FLOAT = 0xFF000000; //First 8 bits of float
const uint32_t EXPONENT_BITS = 0x7F800000; //Exponent bits 2-9 of a float
const uint32_t DEFER_ITEM_BITS = 0xC000; //Bitmask for storing deferred writes of items

const uint32_t ITEM_SLOTS = 256; //Max number of items we expect to track at once
const uint32_t MESSAGE_SIZE = 517; //Size of Message Splitter event
Expand Down Expand Up @@ -977,6 +978,18 @@ class Compressor {
return ((item_id << 8) >> 24);
}

inline unsigned encodeDeferInfo(unsigned u16, unsigned defer) const {
return u16 | (defer << 14);
}

inline unsigned getDeferInfo(unsigned u16) const {
return (u16&DEFER_ITEM_BITS) >> 14;
}

inline unsigned decodeDeferInfo(unsigned u16) const {
return u16 & (~DEFER_ITEM_BITS);
}

inline int32_t lookAheadToFinalizedFrame(char* mem_start, int32_t ref_frame=0) const {
unsigned mem_off = 0;
while(mem_start[mem_off] != Event::BOOKEND) {
Expand Down
5 changes: 0 additions & 5 deletions src/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,6 @@ int sanityCheck(slip::Parser *p, std::string name, std::string path, bool corrup
if (sf.stocks > 99) {
++flag_stocks;
}
if (sf.stocks > r->player[pnum].frame[f-1].stocks) {
++flag_stocks_inc;
}
}
std::string ps = std::to_string(pnum+1);
ASSERT(" Port " + ps + " character is consistent throughout game",flag_char==0,
Expand All @@ -199,8 +196,6 @@ int sanityCheck(slip::Parser *p, std::string name, std::string path, bool corrup
ps << "'s hurt by player >= 8");
ASSERT(" Port " + ps + " stocks always <= 99",flag_stocks==0,
ps << "'s stocks > 99");
ASSERT(" Port " + ps + " stocks never increase",flag_stocks_inc==0,
ps << "'s stocks increase");
}

return 0;
Expand Down
Binary file not shown.
Binary file added test-replays/standard/3-9-0-retcon-item-2.slp.xz
Binary file not shown.
Binary file added test-replays/standard/3-9-0-retcon-item.slp.xz
Binary file not shown.
Binary file added test-replays/standard/3-9-0-retcon-player.slp.xz
Binary file not shown.

0 comments on commit f1c65a8

Please sign in to comment.