retroachievements / raintegration Goto Github PK
View Code? Open in Web Editor NEWThe DLL responsible to integrate emulators with RetroAchievements.org
Home Page: https://retroachievements.org
License: MIT License
The DLL responsible to integrate emulators with RetroAchievements.org
Home Page: https://retroachievements.org
License: MIT License
As a developer, I want to create an achievement with many conditions. The UI doesn't limit me, but things start going back when the serialized string exceeds ~1900 bytes.
There's two major issues contributing to this.
AchievementSet::SaveToFile
uses a 2K buffer to write each line of the XXX-User.txt file. With the additional space taken by the achievement title, description, and other attributes that leaves around 1900 bytes for the MemAddr string.AchievementSet::LoadFromFile
uses a 4K buffer read each line of the XXX-User.txt file, so even if you used another tool (RATools/Notepad) to generate a more complete achievement, it still couldn't be loaded.These methods should be modified to support larger achievements now that the database does.
This is a regression, as this was working as of January. Characters outside of standard extended ASCII, such as ō
, are either replaced by standard ASCII "equivalents" (in this case by o
), or by interrogation marks (at least for Chinese characters, e.g. 好
).
The database was updated recently to support UTF-8 characters in code notes. They are currently not displayed correctly on the website, but this will be fixed in a future update (likely 2.0.0). In the meantime, support should be restored on the side of RAIntegration as well.
A secondary issue is that characters outside of standard ASCII, i.e. extended characters such as ö
, now return an error despite being sent and processed correctly by the server. This can be moved to a separate issue once confirmed if the solution is different from that of the main issue.
@Jamiras
While I was working on the implementation of PauseIf/ResetIf hitcounts for RetroArch I noticed that part of the code is possibly unreachable (highlighting "possibly" because I'm not used to all peculiarities of C++).
The code is in one of those Test()
methods, which you can see below.
I think the code between lines 440 and 502 is unreacheable because it's inside a for
loop and right above line 440 there is a switch-case
clause that always goes to a continue
and the default case is a break
.
RAIntegration/src/RA_Condition.cpp
Lines 402 to 506 in 60731a7
From @Jamiras on March 21, 2018 14:39
As a developer, I would like to track the number of times a value changes to a specific value. To do this currently, I usually write an achievement like "value = X HitCount N and PauseIf delta(value) != X", but this pauses the entire condition group while the "delta(value) != X" clause is true, preventing it from being reset.
What I'd really like is "value ChangesTo X HitCount N" where ChangesTo evaluates as "= X and delta != X". Then I could use it like any other condition and not have to worry about the achievement being paused.
Copied from original issue: RetroAchievements/RASuite#56
@Jamiras, @SyrianBallaS, @GameDragon2k, @leiradel, @ScottFromDerby, @luchaos
Hey guys, I've been talking with @ScottFromDerby and @leiradel about the bright and promising moment we, at RetroAchievements scene, are experiencing. We have a bunch of very skilled guys contributing with the project and this is freaking awesome.
However, we are a bit scattered. Each one of us is making great contributions but each with their own focus. I have a strong conviction that if we make a roadmap and share the same goals, we will achieve better and faster results and also avoid wasting of time on unnecessary work.
That being said, let me list here some goals we are aiming for RAIntegration in a hope that we can direct the efforts to them.
The main reason for it is that currently it's a pain to maintain two codebases for the same thing (here and on RetroArch).
RetroArch users are a big slice of our user base (I dare to say they are more than 50%), and any new feature implemented in RAIntegration must consider compatibility with RetroArch.
The good news is that @leiradel is already working on this front.
UPDATE: here's the repo where this is being worked: https://github.com/RetroAchievements/rcheevos
This will be really cool. Achievement developers will be freed from many of the existing limitations of the current toolkit.
Good news here too: @Jamiras and @leiradel are working on this front.
We still need to make some adjustments on the server side to make it a real thing, but I already started to discuss it with @luchaos. I hope we can solve it soon.
Currently we have RALibretro, but as you may know it lacks some basic features (e.g.: input remapping) and implementing them from scratch is like reinventing the wheel.
The good news here is that @leiradel told me that the leadership of RetroArch is in a good agreement about adding RAIntegration support on the Windows version of RetroArch.
The new RetroArch WIMP GUI should be a good interface to use while developing achievements. It's powered by Qt, and it's exactly the subject of the next topic.
This is a long-term goal. I recognize that this requires a good amount of effort, but I want to highlight this as a goal in a hope to prevent the current development from moving toward a more intensive use of Windows-only solutions.
We know that some libretro guys are interested on this Qt port, and maybe they can help here at some point.
Being able to develop achievements on Linux is like a dream for me and I'm sure that it would also bring many knowledgeable people to the scene (cough @RobLoach cough).
IMHO making the cheevo development toolkit be cross-platform is a huge contribution to its longevity.
This is while testing my enum_to_enumclasses branch to make sure the dirty flags and bitwise operator overloads were working as intended.
Basically all you have to do is enter a non-integral into the points section and it will throw. Probably during WM_CLOSE
and/or IDOK
we should check that "Points" field is populated with an integral.
Kind of like this, it's just an example. There could be better way, just to give an idea.
case WM_CLOSE:
...
auto len{GetWindowTextLength(point_field_handle)};
if(len == 0)
return -1;
auto buf{std::make_unique<TCHAR[]>(len)};
if(GetWindowText(point_field_handle, buf.get(), len) == 0)
return -1;
std::string str{Narrow(tstr.get())};
for(auto& ch : str)
{
if(!std::isdigit(static_cast<int>(ch)))
{
MessageBox(..., _T("The point field must only contain an integral value."), ...);
return -1; // To stop processing it has to be something besides 0, "-1" is arbitrary.
}
}
...
So here's a few other non-critical things I noticed.
Dlg_AchievementEditor::LoadAchievement
Dlg_AchievementEditor::LoadAchievement
sometimes gets called twice in unofficial (did not commit anything).IDC_RA_REVERTSELECTED in RA_Dlg_Achievement.cpp talks about "reverting from file" but the only thing that seems to write to file is IDC_RA_COMMIT_ACH when (g_nActiveAchievementSet == Local).
Using the "Revert selected" button when Unofficial set is selected leads to some unclear behaviour.
IMO it'd make sense if unofficial set actually reverted from server.
As a player, I want an indicator when a challenge achievement is active so I know when not to do things that might disable it, and know when I have done something that does disable it.
This primarily applies to things like no-damage achievements. An icon would be displayed when starting the boss fight, and disappears when damage is taken or the achievement triggers.
From @kdecks on April 8, 2018 5:7
This is probably a hybrid RASuite/RAWeb issue.
This concept is for visual grouping:
+ Penguin Craze
- Penguin Craze I
- Penguin Craze II
- Penguin Craze III
- Penguin Craze IV
- Penguin Craze V
- Penguin Craze VI
Where the + designates collapsible folders and - designates contents hidden after being collapsed.
Each one of the Penguin Craze I through VI would all be normal achievements.
The grouping is to allow devs to more clearly show set structure and help visually simplify game pages. The folder is not an achievement, but would display a percentage of the achievements completed within it. The folder would be able to be expanded and contracted on the game page and inside the emulator. Only one level of folders would be allowed, no sub folders. A default folder icon would be used for visual consistency.
Copied from original issue: RetroAchievements/RASuite#75
From @kdecks on April 8, 2018 5:49
Anyone who wants to can dump incomplete ideas or ideas that have not yet been fleshed out, here, I'll be doing so. (Just as with anything just because I posted it does not mean I OWN the ideas, good ideas are living things that grow on their own!) Also these names are not important and could be called anything that's effectively descriptive.
Flags:
LockIf (single condition if met, will prevent further achievement processing for that group)
UnlockIf (single condition if met, will unlock the unlocked group above)
Achievement Pointers (an achievement or group that if true will add hits to another achievement, this is done somewhat with addhits and addsource)
ResetFirst (reset before pause) OR LatePause
ResetGroup
ResetTask (see task group below)
ALT group alts:
AND groups (All conditions one each group must be true at once)
Task Groups (Similar to AND groups. Once the conditions of a Task are met achievement processing stops, it cannot be paused or reset and remains TRUE. Once the Tasks and the Core group are true the achievement is activated. Can only be reset by a ResetTask)
Other:
Any kind of logic gates
Data sheet (a table of values are stored and retrieved by the achievement editor during the current session)
Offline data sheet (like above, but allows for values to be stored between sessions.) (Would probably need encryption or a hash check to prevent cheating.)
Copied from original issue: RetroAchievements/RASuite#76
From @Jamiras on March 21, 2018 14:55
I was looking at the leaderboard code and noticed the following:
https://github.com/RetroAchievements/RASuite/blame/master/RA_Integration/RA_Leaderboard.cpp#L462
The cancel condition is passed TRUE when it's evaluated, which indicates Test
should return true if any condition is true. This effectively makes the logic be OR
for all conditions in the Cancel field (despite still using '_' as a separator). It looks like it's been that way since it was originally added, which is further supported by the fact that it's the only timeTest
is passed TRUE.
There are currently 171 leaderboards across 22 games that have multiple conditions in their Cancel field, most notably Super Mario Bros and Super Mario Kart.
This behavior seems idiosyncratic (and I don't believe is properly implemented in RetroArch).
I don't know if there's any easy way to fix it. Ideally we'd want to make "_" behave like an and and introduce a new symbol for or (probably "S" like achievements).
But since it's dll behavior, we can't update the existing leaderboards until a new dll is published. Maybe have an interim release that defines both "_" and "S" as OR
, fix all the achievements, then redefine "_" back to AND
in a later release?
Copied from original issue: RetroAchievements/RASuite#58
From @SyrianBallaS on April 3, 2018 19:25
OK, so I noticed the .editorconfig file got accepted. That will definitely help a little bit, but ultimately not solve the root issue of inconsistent style. It is understandable that inconsistencies are undesirable and wish to propose a solution. Some of these come from the reviews in pull request #49, others from myself, and, isocpp. For some insight, I professionaly program in ASP.NET Core so some stylings may differ from isocpp.
On another note (somewhat related), I propose using a package.config/project.json file for binaries to prevent changes to library files (plus it will save space in the repo). Had one a long time ago but deleted the repo that was in it because it was too far gone (incorrect).
The lead engineer (I'm assuming @ScottFromDerby) will have the final say.
This is just a proposal, what's actually going to be used should be formalized in the wiki or a CONTRIBUTING file. This is just to give some ideas.
For a lot of these, you can easily change them using ctrl+alt+f
.
I would prefer data members be in snake-case with a leading underscore (i.e., some_var_
) as encoding type information is redundant.
However, if the majority strongly desire Hungarian Notation that's fine too.
_RA
to make it more consistent. It's defined like this: #define _RA ::ra::
is_equality_comparable
and is_lessthan_comparable
to validate something is comparable it looks for the equality and lessthan operator. It will be important for some enums. If those validations pass in static_assert
you can use using namespace std::rel_ops
(suggest at function scope) to have the rest of the operators done for us. Some other operator overloads might be necessary as well but it's trivial.To check/uncheck, go to Tools->Options->Text Editor->C/C++->Formatting->(*something). *something will depend on what we're trying to do. Some of these might be checked/unchecked already.
To save time I'll just put a screen shot
I'd suggest for everything besides multiline functions and long loops (the initialization part) put a brace on the same line with one space before it.
constexpr
(wasn't fully implemented (still isn't, no constexpr if
) by Microsoft until C++17 but did exist in C++14).#define SOME_STRING "a string"
, consider inline constexpr const char* some_string{"a string"}
constexpr if
`is short for "constant expression" and can only be used on types that can be resolved at compile time.zcstring
which is a wrapper for const char*
which is bounded.std::function
with std::bind
. (creates a function object instead of a pointer, which automatically deallocates).int some_fn(char c, std::string str);
using some_fn_t = decltype(&some_fn);
const auto& another_fn{static_cast(some_fn_t)(some_fn)};
another_fn
is an alias to some_fn
; Did some extra steps because that's for overloads. Overloaded template functions needs parameter forwarding but we'll worry about that later.
using
. Instead of typedef int some_type ;
consider using some_type = int;
.HANDLE_MSG
.Copied from original issue: RetroAchievements/RASuite#73
It would be useful to have a active Hits counter being displayed in the Cheevo Descriptions.
The implementation would work by using ID Requirements in the Description, for example:
Displayed in RetroArch:
Collect 10 (5) Apples.
Nothing changed beside the (5) shows the current number of Hits/collected Apples.
Code could look like following:
Collect 10 [H=2] Apples.
[H=] being the function to read Hits, 2 the ID in the Toolkit (Cheevo Requirement).
Having such function build into the Cheevo Descriptions would allow anyone to quickly open the Cheevo Menu and see all active Hits by browsing through the list of Cheevos, which Cheevo is alive (not reseted yet) or which Item has been collected/which one is missing, for example:
Cheevo List:
Deathless
Finish Stage 1 without falling into a hole (1).
Dragon Parts
Collect Dragon Sword (1), Dragon Shield (1) and Dragon Armor (0).
The Hits in Description could also be displayed on the Game Page / Cheevo Page, updated with the Rich Presence or just being hidden. Having such information on Web would also allow us to use the RP Space on the frontpage for other ingame informations.
From @Jamiras on March 21, 2018 14:51
As a developer, I'd like to mark a HitCount condition as a progress indicator for providing feedback to the user.
Many achievements fall into the category of do something N times, but the user doesn't necessary know how may times he's done it. This feature would enable specific achievements to report progress using the leaderboard UI.
Conceptually, as each achievement is processed, any active achievements with a Progress condition and a non-zero HitCount would calculate their progress percentage. Whichever was closest to completion would show up in the corner like a leaderboard.
For example, if there are achievements for killing 100 enemies and 1000 enemies, both would have a HitCount of 50. 50/100 = 50% and 50/1000=5%, so only the 50/100 would be displayed.
I'm not sure the best way to indicate what the progress is measuring. The best idea I've had is to show a leaderboard startup popup "Challenge available - kill 100 enemies" when the HitCount changes to 1, but if the user is working towards multiple goals at the same time, one might jump ahead of the other and suddenly the progress changes from "50/100" to "5/8" and the user may not remember what he's supposed to be doing 8 times.
By using the Flag field, this logic can be applied to a single condition or a series of AddSource or AddHits conditions. It also eliminates the need to change the UI.
Copied from original issue: RetroAchievements/RASuite#57
Launching RANes
from a search (through the Windows 10 Start Menu for example) causes the following issues to occur:
Logging on and linking a hash to a database entry work correctly.
When launched from Windows Explorer by navigating to the directory of the executable, none of these issues are present.
It would be useful if we could use the Memory Inspector even when offline. The code notes fields could be grayed out when not connected.
Not sure if it's an edge case, but for me it would be extremely useful (and in a discord conversation with @JuliaWolska (aka Salsa) she said it would be useful for her too).
My case: every month I stay for 14 days working at sea. And while there I have internet access in my spare time but it occurs through a restrictive proxy, which blocks retroachievements.org. Therefore, totally unable to access the RetroAchievements menu -> Memory Inspector in the emulators.
I could work on more games if I was able to use Memory Inspector offline.
From @Jamiras on April 1, 2018 13:30
As a developer, I'd like to create a leaderboard whose value is a HitCount condition, not a memory address.
For example, Mario's coin counter resets at 100, so there's no way to create a leaderboard for most coins collected in a playthrough. I'd like the value to be hitcount(coins != delta(coins))
Copied from original issue: RetroAchievements/RASuite#70
As a developer, I don't want to have to code my achievements to handle uninitialized memory while the ROM is being loaded into RAM. There isn't a single valid reason why any achievement should trigger in the first few seconds of starting a game.
As a developer, I'd like to be able to create achievements without an active internet connection. Currently, to access any of the tools, I have to log in first.
Suggest adding a new "Work Offline" menu option to enable the behavior. The user could just click past the "error connecting to retroachievements.org" dialog, or it could be modified to quickly enable the feature. In either case, once enabled, a dialog should be shown letting the user know they're in offline mode and that achievements cannot be earned until they reconnect.
I wanted to get started on developing achievements for a game, and there was some clutter I wanted to get rid of in the Memory Inspector notes. What wasn't made perfectly clear is that, when a note is deleted, it deletes that note for ALL developers as it syncs this change to the server immediately. I created a new RetroAchievements account to test this, and sure enough, those notes are gone. It is eluded to in the documentation, but it is glazed over, and certainly isn't made as urgently clear as it should be.
One, single button, makes this change. All I have to do it click the "delete" button, and BOOM, note gone.
There are NO warnings. Zero indication that I may be screwing over other developers by doing this. Yes, I could save my own bookmarks, but the current UX doesn't make this immediately clear or easy to do. Using notes as though they are bookmarks is much more convenient. I can still see a JSON with usernames and addresses in XYZ-Notes2.txt, but the text content of the "notes" field is empty in all of them.
I realize this is an unpaid, open-source hobby project, but quite frankly, this is unacceptable.
At the absolute minimum, users should be shown a warning that makes it very clear that deleting and saving notes will do the same for other developers. But this won't stop trolls from going through a bunch of games and deleting notes, or even creating fake notes to waste people's time. This is the internet. Stuff like this will absolutely happen.
As for long term fixes, the default server behavior shouldn't be "overwrite" as it currently is. There are many ways to go about this, but one of them is to do the following:
In this setup, I believe the default behavior should be for RAIntegration to NOT download notes by default. Instead, a "download online notes" button should be implemented, or something to that effect. Then, when this button is clicked:
I'm sure this could be fleshed out more. But there really needs to be a discussion about this.
As a side note, does retroachievements.org keep server-side backups of notes? If so, I'd like to request that a certain game be restored to a previous state...
It seems there is an issue with the way RAIntergration calculates the NES ROM (Not File) Checksum in some Games, it doesn't happen that often and it seems only Games with CHR Bank 16 are affected by this as the other NES ROM's Checksums are all correctly calculated.
From what i know is NES the only System on RetroAchievements that is using ROM Checksums, not File Checksums. All the linked MD5s for the NES Games may be correct for RetroAchievements but the ROM Checksums ain't the same when comparing them in other Tools.
This Screenshot shows the RA linked MD5s of the NES ROM (Not File) and the actual Checksums in ROM Hasher. The Game has CHR Bank 16.
And in uCON64.
And here a other Game with CHR Bank 0.
Please disregard and close this if it's a duplicate.
A means for leaderboard syntax to support OR logic; especially for SUBMIT, but also for START and CANCEL as they use the same syntax.
The problem lies especially with SUBMIT: Most games do not have a single, stable address/value for GAME OVER (failure) and GAME OVER (victory), but typically a developer will want to have a submit during failure or victory.
OR logic missing is the number one obstacle I've had while making leaderboards (and I've made more than most developers). Adding a method would save dozens of hours for many devs and often make impossible, possible.
Putting this in here as a heads up, stuff will be done one at a time. Functionality/feature-wise nothing is intended to be different, but the code will be vastly transformed when this is done. If you have any questions or advice please come forward.
N.B.: The emulators will not depend on any of this stuff
std::i/ofstream
will be used instead of FILE*
rapidjson::I/OStreamWrapper
instead instead of rapidjson::FileStream
.EqualityComparable
, LessThanComparable
, Comparable
, also to check if something is a character or a string. (these can be used in a static_assert
)enum
to underlying_type
conversion function (needed if using enum class)
RGB
codeenum classes
comparablestd::array
constexpr
variables and/or function templates where possibleMost of what's below in Memory Management is interrelated, meaning some of a task may have been completed by another. The purpose of separating them into separate tasks to ensure all of what needs to be done will be done completely in an efficient manner.
What's down here will follow the RAII paradigm (resource acquisition is initialization/pointers own resources)
IFileDialog*
you'll see CComPtr<IFileDialog>
, prevents leaksconstexpr
primitive pointers will replaced such as...
FILE*
with i/ofstream (may have been partially done in another task)std::basic_string
(may have been done partially)std::basic_string::data
returns a mutable C string in C++17
ra
objectssmart pointers
for windows/GDI handles
Windows API
(not COM
) can't be owned as-is with the default implementation of std::unique_ptr
, it would take too long to explain it but if you need to know ask away.Basically these two will work with any Window (if top-level) or dialog (RA Dialogs aren't top-level). The window class is the initial registration point for window/dialog/control/etc.
Everything above is preparation to do what's below. I'm fairly confident that there will not be any need for other people to test the stuff above but it wouldn't hurt. Will mostly likely require help testing changes to the modeless dialogs however.
RAIntegration/tests/SearchResults_Tests.cpp
Line 676 in 6b341c2
It seems that what's here is supposed to be a dynamic array but is specified as a pointer to a single object.
Possible Remedies:
std::unique_ptr<unsigned char[]> memory; or
auto memory{std::make_unique<unsigned char[]>(BIG_BLOCK_SIZE)};
with the second one you'll get rid of the reset below it.
edit: TestInitializeFromMemorySixteenBitLargeMemory
below has the same issue.
We could run the std::unique_ptr
ruleset later to catch all of them, those are just the ones I noticed.
It's important because std::default_delete
will call delete
instead of delete[]
which may cause a leak.
From @leiradel on February 25, 2018 14:24
Although one might infer that the RASuite code is licensed under the GPL, since it's both compiled into and dynamically linked to other GPL'ed projects, it's best to be clear about it.
As the RALibretro developer, I'd like to make sure under what terms I can use RA code.
Copied from original issue: RetroAchievements/RASuite#45
The existing News overlay renders the raw HTML that appears in the News section of the home page.
There's enough information in the JSON received from the server to do a decent job of mimicing the server behavior.
In addition to "fixing" the display behavior, the News overlay should be featured in the emulator until the user selects a game.
Alternately, we should stop fetching the News data on startup as most users probably never use it.
An ID of 0 is currently sent to the backend when running an unlinked title. I'm not sure at which point this is triggered, but because of that a user's recently played titles page will contain an empty title with an ID of 0. This doesn't happen prior to the linking dialog being triggered, so it should be possible to avoid subsequent API calls if no title is selected for linking.
Corresponding issue on RAWeb
: RetroAchievements/RAWeb#202
So to resolve conflicts I had to merge RetroAchievements master with my master. Tried to test a few things but exceptions keep on getting thrown. I'll add to this as I find them. I'll try to see what's wrong with them myself. On the master branch of course.
I'll put my findings in another comment per problem I find.
Steps to reproduce:
A Offline Mode would be nice for those of us who have a unstable internet connection or just want to work/play offline and then sync their progress manually to the server, similar to PSN/XBL.
Making the entire developing progress offline compatible and allow to save a set/cheevos in one package-file, which can easily be transfered from one device to another device.
A switch in the Emulators (like Hardcore) which turns on the 'Offline Mode'. Offline Mode would function similar to Softcore but it will require to Sync the earnings to users profile, all progress will be saved locally as well. Also being able to load the one package-file which contains every data for developing and earning cheevos incl. cheevo badges anytime a user want, with saving progress of which cheevo has been earned.
This way users can share their custom sets in form of a single file with other users without uploading their content to the Site.
As a developer, I'd like to create an achievement that requires one or more other achievements to be completed first.
For example, in Mario Kart, I might have achievements for getting gold in Mushroom Cup, Flower Cup, Star Cup, and Special Cup. Then I'd have a meta-achievement for getting the gold in the other four cups. Since the other achievements can be earned independently, the meta-achievement can be earned across multiple sessions.
The implementation would be to change the requirement to be "Achievement 12345 = 1". Which would serialize to something like "@12345=1". Note the new "@" prefix to differentiate it from Delta, Memory and Value types. Something to consider is how to represent this for local development where achievements all have ID=0.
As a developer, I want to be able able to reference memory that may not always be at the same address.
To simplify the explanation, I use the following conventions:
$
is a memory reference
$1234
means the memory at address 0x1234
.0x
is a constant()
indicates order of operations:
$(0x5678 + $1234)
means: take the memory at address 0x1234
, add 0x5678
, and get the memory at the resulting address.There are four scenarios to consider:
$($1234)
$1234
contains 0x5678
, condition should look at $5678
.$($1234 + 12)
$1234
contains 0x5678
, condition should look at $(0x5678 + 12)
, i.e.$5684
$(0x5678 + $1234)
, alternately notated as $(0x5678[$1234])
$1234
contains 6
, condition should look at $(0x5678 + 6)
, i.e. $567E
$(0x5678 + $1234 * 16)
, alternately notated as $(0x5678[$1234*16])
$1234
contains 6
, condition should look at $(0x5678 + 6 * 16)
, i.e. $56D8
In all of these scenarios, a known memory location ($1234
) can be used to determine a dynamic address that the condition depends on. All of the scenarios also fit into the formula: "read memory at address, multiply by scalar, add constant, and read memory at result":
$($1234 * 1 + 0x0000)
$($1234 * 1 + 0x000C)
$($1234 * 1 + 0x5678)
$($1234 * 16 + 0x5678)
As such, the proposal below attempts to fit that formula into the existing framework with the smallest amount of overhead.
A new flag AddAddress
will be used to indicate the result of the current row will be added to the address field of the next row. The comparison column will be changed to a multiplication symbol (fixed).
Direct pointer: Condition $($1234)
is 3
AddAddress Mem 16-bit 0x1234 * Value 1
Mem 8-bit 0x0000 = 3
Indirect pointer: Condition: $($1234 + 12)
is 3
AddAddress Mem 16-bit 0x1234 * Value 1
Mem 8-bit 0x000C = 3
Array index: Condition $(0x5678 + $1234)
is 3
AddAddress Mem 8-bit 0x1234 * Value 1
Mem 8-bit 0x5678 = 3
Scaled array index: Condition $(0x5678 + $1234 * 16)
is 0
AddAddress Mem 8-bit 0x1234 * Value 16
Mem 32-bit 0x5678 = 0
Because the AddAddress
line looks like a condition line (except for the '*'), it won't require a fancy new UI, and should be fairly easy for developers to use (assuming they understand how pointers work in the first place). You can also see illustrated above how easy it would be to specify the size of the pointer (or index) using the existing "16-bit/8-bit" dropdown. The direct pointer example uses a 16-bit pointer. The two array index examples use an 8-bit index. The scaled array index example shows the other side of the coin where a 32-bit value is pulled from the calculated memory address.
This structure also leaves the flag field blank on the condition line, allowing for the use of ResetIf
or PauseIf
constructs dependent on the indirect memory. The Hits
column would be ignored for the AddAddress
lines, but still usable normally on the final condition line.
You can also combine multiple AddAddress
lines to something more complex. Here's a scaled array pointed at by a direct pointer:
AddAddress Mem 16-bit 0x1234 * Value 1
AddAddress Mem 8-bit 0x5678 * Value 16
Mem 16-bit 0x0004 = 3
Which is $($1234 + $0x5678 * 16 + 4) == 3
.
The first line is the pointer to the data structure array.
The second line gets the Nth item of the array, where each item is 16 bytes long.
The third line gets the 16-bit value 4 bytes into the structure.
From @Jamiras on March 21, 2018 15:24
As a developer, I'd like to have a catch-all value in my lookup to minimize repetition:
Lookup:Level
1=Level 1
2=Level 2
3=Level 3
4=Titles
5=Titles
6=Titles
7=Titles
could be simplified to:
Lookup:Level
1=Level 1
2=Level 2
3=Level 3
*=Titles
Copied from original issue: RetroAchievements/RASuite#65
I'm just going to work on other stuff and leave this alone for awhile.
Edit: Since there aren't really any features being added/removed there won't be an SRS, but will still like some feedback.
OK, so it came to my attention that there are several aspects of the project that I either overlooked or had no idea what was required of them. This issue will comprise a series a of questions based on #19 Issue. This may seem overblown for an open-source project but my engineering process here has come into question. So resolve that, I will try do things the "right" way, in accordance to process best-practices.
Maybe in the future
I currently do not have access to set-up a GitHub project on this repository so something reflecting this will be in my fork for organization purposes.
Once the requirements have been gathered I will formalize it into a SRS (Software Requirements Specification) and post it here for review (I don't access to request reviews, add labels, etc.). It will also be in my fork in a "docs" folder but will not be included in pull requests.
From @meleu on April 1, 2018 13:46
Currently Leaderboards don't cancel on Reset.
A cheater can, for example, start a "hi-score on hard difficulty" leaderboard, reset the game, change the difficulty to easy and play.
I consider this a bug.
Copied from original issue: RetroAchievements/RASuite#71
The concept is to create a toggle switch button, alongside the filter!
button that would filter every frame and check your comparison until it's turned off again.
Normally when I'm filtering, I will find myself pressing the filter button sometimes dozens of times before I change the conditions of my filter. This switch would be is a big time saver for achievement devs and make for more accurate results by clearing out addresses that don't fit in the filter for one frame and could otherwise easily be missed.
Already talked to @Jamiras about it briefly, he noted that would probably be necessary to turn off the filter history while this is taking place.
From @Jamiras on March 21, 2018 14:34
As a developer, I'd like to create a PauseIf condition that only pauses the achievement if it happens a certain number of times.
Just like regular conditions, when the PauseIf condition is true, it will increment the counter. When the counter reaches the target, then the condition group (core or alt) will be paused. When the condition is false, the counter is not reset. To reset the counter, a seperate ResetIf condition must be present.
This would allow for achievements like "Complete the level without jumping more than three times". Each jump would increment the counter. When it hits four, the achievement is disabled until the user restarts the level and a ResetIf resets the counter.
PauseIf HitCount could also be used as a makeshift timer. If you know the framerate of your system you can create an achievement like "Complete the level in under 30 seconds" that has a PauseIf level=X HitCount (30*frames).
Existing achievements should be audited. Anything with a PauseIf HitCount(>0) will have to be modified. A PauseIf with a HitCount (even 1) will not unpause itself. As the HitCount clause currently has no effect on the PauseIf conditions, they should be changed to HitCount(0).
Copied from original issue: RetroAchievements/RASuite#55
As a developer, I'd like to know what data is in a memory address by looking at an achievement definition without having to go to the address in the memory viewer.
Suggest showing a tooltip containing the code notes for the address (if present) when hovering over the address.
From @Jamiras on March 21, 2018 15:12
As a developer, I'd like to be able to search memory for string patterns. As I may not know the exact byte->tile mapping used by the game, I'd like to be able to enter a string and have the toolkit look for patterns in the memory that could imply the tile mapping.
For example, searching for "25000" would match "C2 C5 C0 C0 C0" or "06 09 04 04 04" as the second byte is 3 bytes higher than the first, and the last three are the same and two bytes lower than the first.
Copied from original issue: RetroAchievements/RASuite#62
From @Jamiras on March 21, 2018 15:1
As a developer, I want to be able to reset or pause an achievement when multiple conditions are true. Currently, if I apply the ResetIf/PauseIf logic to multiple conditions, the achievement is paused or reset if any are true.
The current UI doesn't really provide an easy way to do this. My best suggestion is a AndNext flag that ANDs the state of the current condition to the result of the next condition:
For example:
AndNext level = 3
ResetIf Character = George
This would only reset if Character = George and level = 3
Similarly:
AndNext level = 3
Character = George HitCount 6
would only increment the HitCount if Character = George and level = 3 were both true.
Copied from original issue: RetroAchievements/RASuite#59
From @Jamiras on March 21, 2018 15:20
As a non-developer, I don't want the buttons for promoting achievements to be active as pushing them will only generate an error. Alternately, a permissions error could be displayed without sending anything to the server.
There is currently no way to determine the user's account type in the toolkit. Changes would have to be made to return it from one of the APIs (probably the login API).
There are other things that could be better handled in the toolkit knowing the user's account type, like ROM linking.
Copied from original issue: RetroAchievements/RASuite#64
From @kdecks on April 8, 2018 3:22
This issues is closely related to RAWeb #105 (Merging ~ Bonus ~ Entries into same game.
@GameDragon2k has been making plans to add some new groups. I'm adding this another other issues for both of us based on some recent discussions.
The plan is to add new Active Achievement Groups
to reflect the needs of the community and to disentangle the various uses of unofficial.
Other
Copied from original issue: RetroAchievements/RASuite#74
From @Jamiras on March 21, 2018 15:16
As a developer, I would like the ability to define a leaderboard using the toolkit. Currently I have to manually generate the expressions and load them into the web page, then restart the game to pull them from the webpage in order to try the leaderboard. As the leaderboard is fully active, submissions are made when the leaderboard submit condition is met. It would be preferable to be able to edit and test leaderboard locally before uploading them to the web page.
Copied from original issue: RetroAchievements/RASuite#63
As a developer, I want to create achievements that compare against 32-bit values.
Currently, if you try to enter a value greater than 0x7FFFFFFF (2147483647) in the achievement editor it is constrained down to 0x7FFFFFFF.
I wasn't able to log in to RA through any of the emulator apps, so I changed my password to a simple one. I was able to log in with this simple password. After changing my password back to a more complex one, I wasn't able to log in again. This made me think it was an issue with not escaping special characters on the login request. I skimmed the source code, and saw that passwords are sent as-is without any checks to see if the characters are valid URL characters. Sure enough, after manually escaping my password and pasting the escaped version into RAIntegration's password field, I was able to log in.
For clarification, please consider the following example:
Let's say this string is my password (it's not my real password!): s3?AJhs:u+S=D<%-]@{O
If I use this string as-is in RAIntegration, my login will fail, even though I would be able to log in to RetroAchievements.org with this very password.
However, if I enter an escaped version into RAIntegration, my login will succeed without issue. So my above password would become:
s3%3FAJhs%3Au%2BS%3DD%3C%25-%5D%40%7BO
So instead of sending this request:
https://retroachievements.org/login_app.php?u=ubergeek77&p=s3?AJhs:u+S=D<%-]@{O
it sends this request:
https://retroachievements.org/login_app.php?u=ubergeek77&p=s3%3FAJhs%3Au%2BS%3DD%3C%25-%5D%40%7BO
and the server can properly read the password and send the appropriate response.
RAIntegration should really be doing this manually, as sending any URL request before properly sanitizing it is bad practice.
Looking at the RA_CheckForUpdate()
in RA_Core.cpp
, it seems that the logic only recognize versions starting with 0.
.
This method is unable to handle RALibretro's versions (currently 1.0.12).
Related to RetroAchievements/RALibretro#25.
I guess there's no need to repeat here what already discussed in discord, right?
Many active members and the leadership agree that the hardcore mode should be enabled by default on the first run of the RAEmus. If users want save/load state, they need to disable hardcore mode.
Just to put very clear, this request is only about making hardcore mode be the default mode on the first run. Nothing more, nothing less.
EDIT:
@Jamiras mentioned on Discord something about showing a message when the user tries to save/load a state while in hardcore mode. Alright, that would be cool too! 👍
From @Jamiras on March 21, 2018 14:26
As a developer, I'd like to create a ResetIf condition that only resets the achievement if it happens a certain number of times.
We already have several examples of "Defeat boss without taking damage", but it's impossible to do something like "Defeat boss without jumping more than three times". To do that, you'd need a counter on the ResetIf condition.
Just like regular conditions, when the ResetIf condition is true, it will increment the counter. When the counter reaches the target, then the achievement will be reset.
Because it's tied to a HitCount, any other ResetIf in the achievement may reset its HitCount, but that's desirable as the entire achievement is supposed to reset if any ResetIf is true.
Existing achievements should be audited. Anything with a ResetIf HitCount(1) will be compatible with the change as the counter will increment to 1 and immediate reset the achievement. Anything with a ResetIf HitCount(>1) will have to be modified. As the HitCount clause currently has no effect on the ResetIf conditions, they should be changed to HitCount(0).
Copied from original issue: RetroAchievements/RASuite#54
From @Jamiras on March 21, 2018 15:3
As a developer, I'd like to explicitly reset all the HitCounts in an achievement to 0. Suggest adding a button to the achievement editor to do so.
Copied from original issue: RetroAchievements/RASuite#60
From @Jamiras on March 21, 2018 15:6
As a developer, I'd like to be able to sort the achievements list by ID, title, points, or possibly even author.
Copied from original issue: RetroAchievements/RASuite#61
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.